Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a very basic virtual desktop module #239

Merged
merged 9 commits into from
Jan 23, 2023
1 change: 1 addition & 0 deletions cmd/fynedesk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fyne.io/fyne/v2/theme"

_ "fyshos.com/fynedesk/modules/composit"
_ "fyshos.com/fynedesk/modules/desktops"
_ "fyshos.com/fynedesk/modules/launcher"
_ "fyshos.com/fynedesk/modules/status"
_ "fyshos.com/fynedesk/modules/systray"
Expand Down
3 changes: 3 additions & 0 deletions desk.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type Desktop interface {

AddShortcut(shortcut *Shortcut, handler func())
ShowMenuAt(menu *fyne.Menu, pos fyne.Position)

Desktop() int
SetDesktop(int)
}

var instance Desktop
Expand Down
4 changes: 4 additions & 0 deletions internal/ui/bar.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ func (b *bar) createIcon(data fynedesk.AppData, win fynedesk.Window) *barIcon {
}

func (b *bar) taskbarIconTapped(win fynedesk.Window) {
if win.Desktop() != fynedesk.Instance().Desktop() {
b.desk.SetDesktop(win.Desktop())
return
}
if !win.Iconic() && win.TopWindow() {
win.Iconify()
return
Expand Down
27 changes: 21 additions & 6 deletions internal/ui/desk.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ type desktop struct {
widgets *widgetPanel
mouse fyne.CanvasObject
root fyne.Window
desk int
}

func (l *desktop) Desktop() int {
return l.desk
}

func (l *desktop) SetDesktop(id int) {
l.desk = id

for _, item := range l.wm.Windows() {
if item.Desktop() == id {
item.Uniconify()
} else {
item.Iconify()
}
}
}

func (l *desktop) Layout(objects []fyne.CanvasObject, size fyne.Size) {
Expand Down Expand Up @@ -283,15 +300,13 @@ func (l *desktop) registerShortcuts() {
func() {
// dummy - the wm handles app switcher
})
fynedesk.Instance().AddShortcut(&fynedesk.Shortcut{Name: "Print Window", KeyName: deskDriver.KeyPrintScreen,
Modifier: fyne.KeyModifierShift},
l.AddShortcut(fynedesk.NewShortcut("Print Window", deskDriver.KeyPrintScreen, fyne.KeyModifierShift),
l.screenshotWindow)
fynedesk.Instance().AddShortcut(&fynedesk.Shortcut{Name: "Print Screen", KeyName: deskDriver.KeyPrintScreen},
l.AddShortcut(fynedesk.NewShortcut("Print Screen", deskDriver.KeyPrintScreen, 0),
l.screenshot)
fynedesk.Instance().AddShortcut(&fynedesk.Shortcut{Name: "Calculator", KeyName: fynedesk.KeyCalculator},
l.AddShortcut(fynedesk.NewShortcut("Calculator", fynedesk.KeyCalculator, 0),
l.calculator)
fynedesk.Instance().AddShortcut(&fynedesk.Shortcut{Name: "Lock screen", KeyName: fyne.KeyL,
Modifier: fynedesk.UserModifier},
l.AddShortcut(fynedesk.NewShortcut("Lock screen", fyne.KeyL, fynedesk.UserModifier),
l.LockScreen)
}

Expand Down
5 changes: 3 additions & 2 deletions internal/ui/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"strings"
"sync"

"fyne.io/fyne/v2"
"fyshos.com/fynedesk"

"fyne.io/fyne/v2"
)

type deskSettings struct {
Expand Down Expand Up @@ -233,7 +234,7 @@ func (d *deskSettings) load() {
d.launcherZoomScale = 2.0
}

moduleNames := fyne.CurrentApp().Preferences().StringWithFallback("modulenames", "Battery|Brightness|Compositor|Sound|Launcher: Calculate|Launcher: Open URLs|Network")
moduleNames := fyne.CurrentApp().Preferences().StringWithFallback("modulenames", "Battery|Brightness|Compositor|Sound|Launcher: Calculate|Launcher: Open URLs|Network|Virtual Desktops")
if moduleNames != "" {
d.moduleNames = strings.Split(moduleNames, "|")
}
Expand Down
9 changes: 6 additions & 3 deletions internal/ui/switcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ func (s *Switcher) loadIcons(list []fynedesk.Window) []fyne.CanvasObject {
var ret []fyne.CanvasObject

for _, item := range list {
if item.Desktop() != fynedesk.Instance().Desktop() {
continue
}
ret = append(ret, newSwitchIcon(s, item))
}

Expand All @@ -220,12 +223,12 @@ func (s *Switcher) HideCancel() {
}

func showAppSwitcherAt(off int, wins []fynedesk.Window, prov fynedesk.ApplicationProvider) *Switcher {
if len(wins) <= 1 { // don't actually show
s := &Switcher{provider: prov}
s.icons = s.loadIcons(wins)
if len(s.icons) <= 1 { // don't actually show if only 1 is visible
return nil
}

s := &Switcher{provider: prov}
s.icons = s.loadIcons(wins)
s.loadUI("Window switcher " + SkipTaskbarHint)
if off < 0 {
off = len(s.icons) + off // plus a negative is minus
Expand Down
29 changes: 28 additions & 1 deletion internal/x11/win/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ type client struct {

frame *frame
wm x11.XWM
desk int
}

// NewClient creates a new X11 client for the specified window ID and X window manager
func NewClient(win xproto.Window, wm x11.XWM) x11.XWin {
c := &client{win: win, wm: wm}
c := &client{win: win, wm: wm, desk: fynedesk.Instance().Desktop()}
err := xproto.ChangeWindowAttributesChecked(wm.Conn(), win, xproto.CwEventMask,
[]uint32{xproto.EventMaskPropertyChange | xproto.EventMaskEnterWindow | xproto.EventMaskLeaveWindow |
xproto.EventMaskVisibilityChange}).Check()
Expand Down Expand Up @@ -123,6 +124,24 @@ func (c *client) Close() {
}
}

func (c *client) Desktop() int {
return c.desk
}

func (c *client) SetDesktop(id int) {
if c.desk == id {
return
}

d := fynedesk.Instance()
c.desk = id
if id == d.Desktop() {
c.Uniconify()
} else {
c.Iconify()
}
}

func (c *client) Expose() {
if c.frame == nil {
return
Expand Down Expand Up @@ -156,6 +175,10 @@ func (c *client) Fullscreened() bool {
}

func (c *client) Iconify() {
if c.iconic {
return
}

c.stateMessage(icccm.StateIconic)
windowStateSet(c.wm.X(), c.win, icccm.StateIconic)
}
Expand Down Expand Up @@ -338,6 +361,10 @@ func (c *client) Unfullscreen() {
}

func (c *client) Uniconify() {
if !c.iconic {
return
}

c.stateMessage(icccm.StateNormal)
windowStateSet(c.wm.X(), c.win, icccm.StateNormal)
}
Expand Down
14 changes: 13 additions & 1 deletion internal/x11/wm/desk.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,14 @@ func (x *x11WM) bindShortcuts(win xproto.Window) {
}

func (x *x11WM) keyNameToCode(n fyne.KeyName) xproto.Keycode {
keybind.Initialize(x.x)
switch n {
case fyne.KeySpace:
return keyCodeSpace
case fyne.KeyLeft:
return keyCodeLeft
case fyne.KeyRight:
return keyCodeRight
case deskDriver.KeyPrintScreen:
return keyCodePrintScreen
case fyne.KeyTab:
Expand All @@ -321,11 +326,18 @@ func (x *x11WM) keyNameToCode(n fyne.KeyName) xproto.Keycode {
case fynedesk.KeyVolumeUp:
return keyCodeVolumeMore
case fyne.KeyL:
keybind.Initialize(x.x)
codes := keybind.StrToKeycodes(x.x, "L")
return codes[0]
}

for i := 0; i <= 9; i++ {
id := strconv.Itoa(i)
if n == fyne.KeyName(id) {
codes := keybind.StrToKeycodes(x.x, id)
return codes[0]
}
}

return 0
}

Expand Down
37 changes: 24 additions & 13 deletions internal/x11/wm/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/BurntSushi/xgbutil/xprop"

"fyne.io/fyne/v2"

"fyshos.com/fynedesk"
"fyshos.com/fynedesk/internal/notify"
"fyshos.com/fynedesk/internal/x11"
Expand Down Expand Up @@ -39,7 +40,12 @@ func (x *x11WM) handleActiveWin(ev xproto.ClientMessageEvent) {
}
windowActiveSet(x.x, ev.Window)
if canFocus {
err = xproto.SetInputFocusChecked(x.x.Conn(), 2, ev.Window, xproto.TimeCurrentTime).Check()
if c := x.clientForWin(ev.Window); c != nil && c.Iconic() {
return // don't try to focus iconic windows
}

// ask for focus, when it is lost return to root window
err = xproto.SetInputFocusChecked(x.x.Conn(), 1, ev.Window, xproto.TimeCurrentTime).Check()
if err != nil {
fyne.LogError("Could not set focus", err)
return
Expand Down Expand Up @@ -179,18 +185,23 @@ func (x *x11WM) handleKeyPress(ev xproto.KeyPressEvent) {
if ev.Detail == keyCodeTab {
x.showOrSelectAppSwitcher(shift)
return
} else if ev.Detail == keyCodeEscape {
x.cancelAppSwitcher()
return
} else if ev.Detail == keyCodeReturn || ev.Detail == keyCodeEnter {
x.applyAppSwitcher()
return
} else if ev.Detail == keyCodeLeft {
x.previousAppSwitcher()
return
} else if ev.Detail == keyCodeRight {
x.nextAppSwitcher()
return
}

if switcherInstance != nil {
switch ev.Detail {
case keyCodeEscape:
x.cancelAppSwitcher()
return
case keyCodeReturn, keyCodeEnter:
x.applyAppSwitcher()
return
case keyCodeLeft:
x.previousAppSwitcher()
return
case keyCodeRight:
x.nextAppSwitcher()
return
}
}
}
numlock := ev.State & xproto.ModMask2
Expand Down
70 changes: 70 additions & 0 deletions modules/desktops/desktops.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package desktops

import (
"strconv"

"fyne.io/fyne/v2"

"fyshos.com/fynedesk"
)

const deskCount = 4

var desksMeta = fynedesk.ModuleMetadata{
Name: "Virtual Desktops",
NewInstance: newDesktops,
}

type desktops struct {
current int
gui *pager
}

func (d *desktops) Destroy() {
}

func (d *desktops) Metadata() fynedesk.ModuleMetadata {
return desksMeta
}

func (d *desktops) Shortcuts() map[*fynedesk.Shortcut]func() {
mapping := make(map[*fynedesk.Shortcut]func(), deskCount+2)
for i := 0; i < deskCount; i++ {
id := strconv.Itoa(i + 1)
deskID := i
mapping[&fynedesk.Shortcut{Name: "Switch to Desktop " + id, KeyName: fyne.KeyName(id), Modifier: fynedesk.UserModifier}] = func() {
d.setDesktop(deskID)
}
}

mapping[&fynedesk.Shortcut{Name: "Switch to Previous Desktop", KeyName: fyne.KeyLeft, Modifier: fynedesk.UserModifier}] = func() {
if d.current == 0 {
return
}
d.setDesktop(d.current - 1)
}
mapping[&fynedesk.Shortcut{Name: "Switch to Next Desktop", KeyName: fyne.KeyRight, Modifier: fynedesk.UserModifier}] = func() {
if d.current == deskCount-1 {
return
}
d.setDesktop(d.current + 1)
}
return mapping
}

func (d *desktops) StatusAreaWidget() fyne.CanvasObject {
return d.gui.ui
}

func (d *desktops) setDesktop(id int) {
d.current = id
fynedesk.Instance().SetDesktop(id)
d.gui.refresh()
}

// newDesktops creates a new module that will manage virtual desktops and display a pager widget.
func newDesktops() fynedesk.Module {
d := &desktops{}
d.gui = newPager(d)
return d
}
7 changes: 7 additions & 0 deletions modules/desktops/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package desktops

import "fyshos.com/fynedesk"

func init() {
fynedesk.RegisterModule(desksMeta)
}
Loading