Skip to content

Commit

Permalink
Fix #11 - Add --subscribe-node-count to cli
Browse files Browse the repository at this point in the history
  • Loading branch information
diogox committed Mar 6, 2021
1 parent 35b18f5 commit 79a7e01
Show file tree
Hide file tree
Showing 12 changed files with 526 additions and 92 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bspm -d &

All commands are prefixed with the subcommand `monocle`.

#### Actions
Toggle this mode for the desktop you're currently on:
```shell
bspm monocle --toggle
Expand All @@ -38,6 +39,18 @@ Cycle to the previous node:
bspm monocle --prev
```

#### Subscriptions
Subscriptions are useful to create interactions with bspm's state.

You can, for example, setup your status bar to show an icon when monocle mode is active, and how many nodes are open.

Subscribe to number of nodes in the current desktop's monocle mode (number also updates as desktop focus changes):
```shell
bspm monocle --subscribe-node-count
```
*This will return `-1` if monocle mode is disabled.
Bear in mind that a desktop in monocle mode can still have `0` nodes.*

That's it!

---
Expand Down
49 changes: 39 additions & 10 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package cli
import (
"errors"
"fmt"
"io"
"os"


"github.com/diogox/bspm/internal/subscription"

"github.com/fatih/color"
Expand All @@ -19,11 +19,12 @@ import (
)

const (
flagKeyDaemon = "daemon"
flagKeyVerbose = "verbose"
flagKeyMonocleToggle = "toggle"
flagKeyMonocleNext = "next"
flagKeyMonoclePrev = "prev"
flagKeyDaemon = "daemon"
flagKeyVerbose = "verbose"
flagKeyMonocleToggle = "toggle"
flagKeyMonocleNext = "next"
flagKeyMonoclePrev = "prev"
flagKeyMonocleSubscribeNodeCount = "subscribe-node-count"
)

type app struct {
Expand Down Expand Up @@ -70,6 +71,10 @@ func New(logger *zap.Logger, version string) app {
Name: flagKeyMonoclePrev,
Usage: "Shows the previous node in the transparent monocle workflow",
},
&cli.BoolFlag{
Name: flagKeyMonocleSubscribeNodeCount,
Usage: "Returns the number of nodes in the transparent monocle workflow, every time it changes",
},
},
Action: func(ctx *cli.Context) error {
c, err := grpc.NewClient()
Expand All @@ -82,14 +87,15 @@ func New(logger *zap.Logger, version string) app {
}

var (
isToggle = ctx.Bool(flagKeyMonocleToggle)
isNext = ctx.Bool(flagKeyMonocleNext)
isPrev = ctx.Bool(flagKeyMonoclePrev)
isToggle = ctx.Bool(flagKeyMonocleToggle)
isNext = ctx.Bool(flagKeyMonocleNext)
isPrev = ctx.Bool(flagKeyMonoclePrev)
isSubscribeNodeCount = ctx.Bool(flagKeyMonocleSubscribeNodeCount)
)

switch {
case isToggle:
if _, err := c.ToggleMonocleMode(ctx.Context, &empty.Empty{}); err != nil {
if _, err := c.MonocleModeToggle(ctx.Context, &empty.Empty{}); err != nil {
return fmt.Errorf("failed to toggle monocle mode: %w", err)
}
case isPrev:
Expand All @@ -108,6 +114,29 @@ func New(logger *zap.Logger, version string) app {
if _, err := c.MonocleModeCycle(ctx.Context, req); err != nil {
return fmt.Errorf("failed to cycle to next node in monocle mode: %w", err)
}
case isSubscribeNodeCount:
req := &bspm.MonocleModeSubscribeRequest{
Type: bspm.MonocleModeSubscriptionType_MONOCLE_MODE_SUBSCRIPTION_TYPE_NODE_COUNT,
}

subscription, err := c.MonocleModeSubscribe(ctx.Context, req)
if err != nil {
return fmt.Errorf("failed to subscribe to node count in monocle mode: %w", err)
}

// TODO: Graceful shutdown
for {
msg, err := subscription.Recv()
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}

return fmt.Errorf("failed to receive message from monocle mode node count subscription: %w", err)
}

fmt.Println(msg.GetNodeCount())
}
default:
return errors.New("unexpected error")
}
Expand Down
1 change: 1 addition & 0 deletions internal/cli/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func runDaemon(logger *log.Logger, subscriptionManager subscription.Manager) err
bspwmnode.NewService(bspwmClient),
bspwmevent.NewManager(logger, bspwmClient),
),
subscriptionManager,
)
if err != nil {
return err
Expand Down
16 changes: 4 additions & 12 deletions internal/feature/transparent_monocle/state/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@
package state

import (
"errors"
"sync"

"github.com/diogox/bspc-go"

"github.com/diogox/bspm/internal/feature/transparent_monocle/topic"
"github.com/diogox/bspm/internal/subscription"
)

const (
topicMonocleEnabled subscription.Topic = "monocle_enabled"
topicMonocleDisabled subscription.Topic = "monocle_disabled"
topicMonocleStateChanged subscription.Topic = "monocle_state_changed"
)

var ErrNotFound = errors.New("not found")

type (
Manager interface {
Get(desktopID bspc.ID) (State, bool)
Expand Down Expand Up @@ -60,12 +52,12 @@ func (m manager) Set(desktopID bspc.ID, st State) {

if _, ok := m.desktops[desktopID]; !ok {
m.desktops[desktopID] = st
m.subscriptions.Publish(topicMonocleEnabled, st)
m.subscriptions.Publish(topic.MonocleEnabled, st)
return
}

m.desktops[desktopID] = st
m.subscriptions.Publish(topicMonocleStateChanged, st)
m.subscriptions.Publish(topic.MonocleStateChanged, st)
}

func (m manager) Delete(desktopID bspc.ID) {
Expand All @@ -75,5 +67,5 @@ func (m manager) Delete(desktopID bspc.ID) {
prevState := m.desktops[desktopID]

delete(m.desktops, desktopID)
m.subscriptions.Publish(topicMonocleDisabled, prevState)
m.subscriptions.Publish(topic.MonocleDisabled, prevState)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ import (
"github.com/diogox/bspc-go"
)

const (
ExportMonocleEnabled = topicMonocleEnabled
ExportMonocleDisabled = topicMonocleDisabled
ExportMonocleStateChanged = topicMonocleStateChanged
)

func (m manager) WithState(newState map[bspc.ID]State) manager {
m.desktops = newState
return m
Expand Down
7 changes: 4 additions & 3 deletions internal/feature/transparent_monocle/state/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/diogox/bspm/internal/feature/transparent_monocle/state"
"github.com/diogox/bspm/internal/feature/transparent_monocle/topic"
"github.com/diogox/bspm/internal/subscription"
)

Expand Down Expand Up @@ -54,7 +55,7 @@ func TestTransparentMonocle_Set(t *testing.T) {
)

mockSubscriptions := subscription.NewMockManager(ctrl)
mockSubscriptions.EXPECT().Publish(state.ExportMonocleEnabled, st)
mockSubscriptions.EXPECT().Publish(topic.MonocleEnabled, st)

state.NewTransparentMonocle(mockSubscriptions).WithState(initial).Set(desktopID, st)

Expand All @@ -78,7 +79,7 @@ func TestTransparentMonocle_Set(t *testing.T) {
)

mockSubscriptions := subscription.NewMockManager(ctrl)
mockSubscriptions.EXPECT().Publish(state.ExportMonocleStateChanged, st)
mockSubscriptions.EXPECT().Publish(topic.MonocleStateChanged, st)

state.NewTransparentMonocle(mockSubscriptions).WithState(initial).Set(desktopID, st)

Expand Down Expand Up @@ -106,7 +107,7 @@ func TestTransparentMonocle_Delete(t *testing.T) {
)

mockSubscriptions := subscription.NewMockManager(ctrl)
mockSubscriptions.EXPECT().Publish(state.ExportMonocleDisabled, st)
mockSubscriptions.EXPECT().Publish(topic.MonocleDisabled, st)

state.NewTransparentMonocle(mockSubscriptions).WithState(initial).Delete(desktopID)
})
Expand Down
10 changes: 10 additions & 0 deletions internal/feature/transparent_monocle/topic/topics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package topic

import "github.com/diogox/bspm/internal/subscription"

const (
MonocleEnabled subscription.Topic = "monocle_enabled"
MonocleDisabled subscription.Topic = "monocle_disabled"
MonocleStateChanged subscription.Topic = "monocle_state_changed"
MonocleDesktopFocusChanged subscription.Topic = "monocle_focused_desktop_changed"
)
85 changes: 79 additions & 6 deletions internal/feature/transparent_monocle/transparent_monocle.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
"github.com/diogox/bspc-go"
"go.uber.org/zap"

"github.com/diogox/bspm/internal/feature/transparent_monocle/topic"
"github.com/diogox/bspm/internal/subscription"

"github.com/diogox/bspm/internal/bspwm"
"github.com/diogox/bspm/internal/bspwm/filter"

Expand All @@ -22,12 +25,14 @@ type (
ToggleCurrentDesktop() error
FocusPreviousHiddenNode() error
FocusNextHiddenNode() error
SubscribeNodeCount() chan int
}

transparentMonocle struct {
logger *log.Logger
service bspwm.Service
desktops state.Manager
logger *log.Logger
service bspwm.Service
desktops state.Manager
subscriptions subscription.Manager
}
)

Expand All @@ -37,6 +42,7 @@ func Start(
logger *log.Logger,
desktops state.Manager,
service bspwm.Service,
subscriptions subscription.Manager,
) (Feature, func(), error) {
service.Events().On(bspc.EventTypeNodeAdd, func(eventPayload interface{}) error {
payload, ok := eventPayload.(bspc.EventNodeAdd)
Expand Down Expand Up @@ -263,15 +269,23 @@ func Start(
return nil
})

// TODO: I should extract these callback definitions to where they make the most sense.
// Needed to trigger subscriptions when changing monocle mode instances (between desktops).
service.Events().On(bspc.EventTypeDesktopFocus, func(eventPayload interface{}) error {
subscriptions.Publish(topic.MonocleDesktopFocusChanged, nil)
return nil
})

cancelFunc, err := service.Events().Start()
if err != nil {
return nil, nil, fmt.Errorf("failed to start event manager")
}

return &transparentMonocle{
logger: logger,
service: service,
desktops: desktops,
logger: logger,
service: service,
desktops: desktops,
subscriptions: subscriptions,
}, cancelFunc, nil
}

Expand Down Expand Up @@ -533,6 +547,65 @@ func (tm transparentMonocle) FocusNextHiddenNode() error {
return nil
}

func (tm transparentMonocle) SubscribeNodeCount() chan int {
var (
stateCh = tm.subscriptions.Subscribe(topic.MonocleStateChanged)
enabledCh = tm.subscriptions.Subscribe(topic.MonocleEnabled)
disabledCh = tm.subscriptions.Subscribe(topic.MonocleDisabled)
desktopFocusCh = tm.subscriptions.Subscribe(topic.MonocleDesktopFocusChanged)
)

var (
countCh = make(chan int, 1)
publishCountFromState = func(st state.State) {
count := len(st.HiddenNodeIDs)
if st.SelectedNodeID != nil {
count++
}

countCh <- count
}
getAndPublishCount = func() {
focusedDesktop, err := tm.service.Desktops().Get(filter.DesktopFocused)
if err == nil { // TODO: Log if there's an error?
currentState, ok := tm.desktops.Get(focusedDesktop.ID)
switch ok {
case true:
publishCountFromState(currentState)
case false:
// Mode is disabled
countCh <- -1
}
}
}
)

// Publish current number of nodes
getAndPublishCount()

go func() {
for {
select {
case payload := <-stateCh:
updatedState := payload.(state.State)
publishCountFromState(updatedState)

case payload := <-enabledCh:
updatedState := payload.(state.State)
publishCountFromState(updatedState)

case <-desktopFocusCh:
getAndPublishCount()

case <-disabledCh:
countCh <- -1
}
}
}()

return countCh
}

func removeFromSlice(slice []bspc.ID, toRemove bspc.ID) []bspc.ID {
ss := make([]bspc.ID, 0, len(slice)-1)
for _, id := range slice {
Expand Down
Loading

0 comments on commit 79a7e01

Please sign in to comment.