From c46e61bb07a6354bb3aede26f3e1e70d6f5717f5 Mon Sep 17 00:00:00 2001 From: user Date: Fri, 22 Apr 2022 17:34:33 -0700 Subject: [PATCH] Add support for hovering buttons --- src/atoms.nim | 10 ++++- src/events/clientmessage.nim | 78 ++++++++++++++++++++++++++++++++++++ src/types.nim | 7 ++-- src/wm.nim | 78 ++++++++++++++++++++++++++++++------ src/wormc.nim | 18 +++++++++ 5 files changed, 172 insertions(+), 19 deletions(-) diff --git a/src/atoms.nim b/src/atoms.nim index ff13067..7e23b3d 100644 --- a/src/atoms.nim +++ b/src/atoms.nim @@ -73,11 +73,17 @@ type IpcButtonSize = "WORM_IPC_BUTTON_SIZE", IpcRootMenu = "WORM_IPC_ROOT_MENU", IpcCloseActivePath = "WORM_IPC_CLOSE_ACTIVE_PATH", - IpcCloseInactivePath = "WORM_IPC_CLOSE_INACTIVE PATH", + IpcCloseInactivePath = "WORM_IPC_CLOSE_INACTIVE_PATH", + IpcCloseActiveHoveredPath = "WORM_IPC_CLOSE_ACTIVE_HOVERED_PATH" + IpcCloseInactiveHoveredPath = "WORM_IPC_CLOSE_INACTIVE_HOVERED_PATH", IpcMaximizeActivePath = "WORM_IPC_MAXIMIZE_ACTIVE_PATH", IpcMaximizeInactivePath = "WORM_IPC_MAXIMIZE_INACTIVE_PATH", - IpcMinimizeACtivePath = "WORM_IPC_MINIMIZE_ACTIVE_PATH", + IpcMaximizeActiveHoveredPath = "WORM_IPC_MAXIMIZE_ACTIVE_HOVERED_PATH", + IpcMaximizeInactiveHoveredPath = "WORM_IPC_MAXIMIZE_INACTIVE_HOVERED_PATH", + IpcMinimizeActivePath = "WORM_IPC_MINIMIZE_ACTIVE_PATH", IpcMinimizeInactivePath = "WORM_IPC_MINIMIZE_INACTIVE_PATH", + IpcMinimizeActiveHoveredPath = "WORM_IPC_MINIMIZE_ACTIVE_HOVERED_PATH", + IpcMinimizeInactiveHoveredPath = "WORM_IPC_MINIMIZE_INACTIVE_HOVERED_PATH", IpcMaximizeClient = "WORM_IPC_MAXIMIZE_CLIENT", IpcMinimizeClient = "WORM_IPC_MINIMIZE_CLIENT", IpcDecorationDisable = "WORM_IPC_DECORATION_DISABLE" diff --git a/src/events/clientmessage.nim b/src/events/clientmessage.nim index c10f3ee..be47472 100644 --- a/src/events/clientmessage.nim +++ b/src/events/clientmessage.nim @@ -467,6 +467,32 @@ proc handleClientMessage*(self: var Wm; ev: XClientMessageEvent) = if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: XFreeStringList cast[ptr cstring](fontList) discard XFree fontProp.value + elif ev.data.l[0] == clong self.ipcAtoms[IpcCloseActiveHoveredPath]: + var fontProp: XTextProperty + var fontList: ptr UncheckedArray[cstring] + var n: cint + discard self.dpy.XGetTextProperty(self.root, addr fontProp, self.ipcAtoms[ + IpcCloseActiveHoveredPath]) + let err = self.dpy.XmbTextPropertyToTextList(addr fontProp, cast[ + ptr ptr cstring](addr fontList), addr n) + log "Changing active hovered close path to " & $fontList[0] + self.config.closePaths[bsActiveHover] = $fontList[0] + if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: + XFreeStringList cast[ptr cstring](fontList) + discard XFree fontProp.value + elif ev.data.l[0] == clong self.ipcAtoms[IpcCloseInactiveHoveredPath]: + var fontProp: XTextProperty + var fontList: ptr UncheckedArray[cstring] + var n: cint + discard self.dpy.XGetTextProperty(self.root, addr fontProp, self.ipcAtoms[ + IpcCloseInactiveHoveredPath]) + let err = self.dpy.XmbTextPropertyToTextList(addr fontProp, cast[ + ptr ptr cstring](addr fontList), addr n) + log "Changing inactive hovered close path to " & $fontList[0] + self.config.closePaths[bsInactiveHover] = $fontList[0] + if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: + XFreeStringList cast[ptr cstring](fontList) + discard XFree fontProp.value elif ev.data.l[0] == clong self.ipcAtoms[IpcMaximizeActivePath]: var fontProp: XTextProperty var fontList: ptr UncheckedArray[cstring] @@ -493,6 +519,32 @@ proc handleClientMessage*(self: var Wm; ev: XClientMessageEvent) = if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: XFreeStringList cast[ptr cstring](fontList) discard XFree fontProp.value + elif ev.data.l[0] == clong self.ipcAtoms[IpcMaximizeActiveHoveredPath]: + var fontProp: XTextProperty + var fontList: ptr UncheckedArray[cstring] + var n: cint + discard self.dpy.XGetTextProperty(self.root, addr fontProp, self.ipcAtoms[ + IpcMaximizeActiveHoveredPath]) + let err = self.dpy.XmbTextPropertyToTextList(addr fontProp, cast[ + ptr ptr cstring](addr fontList), addr n) + log "Changing active maximize hovered path to " & $fontList[0] + self.config.maximizePaths[bsActiveHover] = $fontList[0] + if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: + XFreeStringList cast[ptr cstring](fontList) + discard XFree fontProp.value + elif ev.data.l[0] == clong self.ipcAtoms[IpcMaximizeInactiveHoveredPath]: + var fontProp: XTextProperty + var fontList: ptr UncheckedArray[cstring] + var n: cint + discard self.dpy.XGetTextProperty(self.root, addr fontProp, self.ipcAtoms[ + IpcMaximizeInactiveHoveredPath]) + let err = self.dpy.XmbTextPropertyToTextList(addr fontProp, cast[ + ptr ptr cstring](addr fontList), addr n) + log "Changing inactive maximize hovered path to " & $fontList[0] + self.config.maximizePaths[bsInactiveHover] = $fontList[0] + if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: + XFreeStringList cast[ptr cstring](fontList) + discard XFree fontProp.value elif ev.data.l[0] == clong self.ipcAtoms[IpcMinimizeActivePath]: var fontProp: XTextProperty var fontList: ptr UncheckedArray[cstring] @@ -519,6 +571,32 @@ proc handleClientMessage*(self: var Wm; ev: XClientMessageEvent) = if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: XFreeStringList cast[ptr cstring](fontList) discard XFree fontProp.value + elif ev.data.l[0] == clong self.ipcAtoms[IpcMinimizeActiveHoveredPath]: + var fontProp: XTextProperty + var fontList: ptr UncheckedArray[cstring] + var n: cint + discard self.dpy.XGetTextProperty(self.root, addr fontProp, self.ipcAtoms[ + IpcMinimizeActiveHoveredPath]) + let err = self.dpy.XmbTextPropertyToTextList(addr fontProp, cast[ + ptr ptr cstring](addr fontList), addr n) + log "Changing active minimize hovered path to " & $fontList[0] + self.config.minimizePaths[bsActiveHover] = $fontList[0] + if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: + XFreeStringList cast[ptr cstring](fontList) + discard XFree fontProp.value + elif ev.data.l[0] == clong self.ipcAtoms[IpcMinimizeInactiveHoveredPath]: + var fontProp: XTextProperty + var fontList: ptr UncheckedArray[cstring] + var n: cint + discard self.dpy.XGetTextProperty(self.root, addr fontProp, self.ipcAtoms[ + IpcMinimizeInactiveHoveredPath]) + let err = self.dpy.XmbTextPropertyToTextList(addr fontProp, cast[ + ptr ptr cstring](addr fontList), addr n) + log "Changing inactive minimize hovered path to " & $fontList[0] + self.config.minimizePaths[bsInactiveHover] = $fontList[0] + if err >= Success and n > 0 and fontList != nil and fontList[0] != nil: + XFreeStringList cast[ptr cstring](fontList) + discard XFree fontProp.value elif ev.data.l[0] == clong self.ipcAtoms[IpcDecorationDisable]: var fontProp: XTextProperty var fontList: ptr UncheckedArray[cstring] diff --git a/src/types.nim b/src/types.nim index 244e7b2..d3b3124 100644 --- a/src/types.nim +++ b/src/types.nim @@ -34,6 +34,7 @@ type beforeGeomMax*: Option[Geometry] maximized*: bool minimized*: bool + ButtonPaths = array[ButtonState, string] Config* = object borderActivePixel*, borderInactivePixel*, borderWidth*: uint frameActivePixel*, frameInactivePixel*, frameHeight*: uint @@ -45,13 +46,11 @@ type frameParts*: tuple[left, center, right: seq[FramePart]] buttonSize*: uint # always square FOR NOW rootMenu*: string - closePaths*: array[ButtonState, string] - maximizePaths*: array[ButtonState, string] - minimizePaths*: array[ButtonState, string] + closePaths*, minimizePaths*, maximizePaths*: ButtonPaths TagSet* = array[9, bool] # distinct proc defaultTagSet*: TagSet = [true, false, false, false, false, false, false, - false, false] + false, false] # put the user on tag 1 when the wm starts. proc switchTag*(self: var TagSet; tag: uint8): void = for i, _ in self: self[i] = false diff --git a/src/wm.nim b/src/wm.nim index 76535b1..abb1ee0 100644 --- a/src/wm.nim +++ b/src/wm.nim @@ -310,7 +310,8 @@ proc renderTop*(self: var Wm; client: var Client) = buttonStateOrig if not fileExists self.config.closePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.closePaths[buttonState]: continue discard self.dpy.XMapWindow client.frame.close @@ -347,8 +348,16 @@ proc renderTop*(self: var Wm; client: var Client) = of fpMaximize: + buttonState = if client.frame.maximizeHovered and buttonStateOrig == bsActive: + bsActiveHover + elif client.frame.maximizeHovered and buttonStateOrig == bsInactive: + bsInactiveHover + else: + buttonStateOrig + if not fileExists self.config.maximizePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.maximizePaths[buttonState]: continue discard self.dpy.XMapWindow client.frame.maximize @@ -384,8 +393,16 @@ proc renderTop*(self: var Wm; client: var Client) = self.XPutImage(image, client.frame.maximize, gc) of fpMinimize: + buttonState = if client.frame.minimizeHovered and buttonStateOrig == bsActive: + bsActiveHover + elif client.frame.minimizeHovered and buttonStateOrig == bsInactive: + bsInactiveHover + else: + buttonStateOrig + if not fileExists self.config.minimizePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.minimizePaths[buttonState]: continue discard self.dpy.XMapWindow client.frame.minimize @@ -449,7 +466,8 @@ proc renderTop*(self: var Wm; client: var Client) = buttonStateOrig if not fileExists self.config.closePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.closePaths[buttonState]: continue let image = self.XCreateImage(self.config.closePaths[buttonState], fp, attr) @@ -499,8 +517,16 @@ proc renderTop*(self: var Wm; client: var Client) = of fpMaximize: + buttonState = if client.frame.maximizeHovered and buttonStateOrig == bsActive: + bsActiveHover + elif client.frame.maximizeHovered and buttonStateOrig == bsInactive: + bsInactiveHover + else: + buttonStateOrig + if not fileExists self.config.maximizePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.maximizePaths[buttonState]: continue discard self.dpy.XMapWindow client.frame.maximize @@ -541,8 +567,16 @@ proc renderTop*(self: var Wm; client: var Client) = self.XPutImage(image, client.frame.maximize, gc) of fpMinimize: + buttonState = if client.frame.minimizeHovered and buttonStateOrig == bsActive: + bsActiveHover + elif client.frame.minimizeHovered and buttonStateOrig == bsInactive: + bsInactiveHover + else: + buttonStateOrig + if not fileExists self.config.minimizePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.minimizePaths[buttonState]: continue let image = self.XCreateImage(self.config.minimizePaths[buttonState], fp, attr) @@ -570,7 +604,6 @@ proc renderTop*(self: var Wm; client: var Client) = (extent.width div 2) + btnXOffset + textXOffset + btnSize elif i == 2: # ez - echo "HIT" btnSize * 2 elif (i == 1 and centerFrames.len >= 3 and (centerFrames[0] == fpTitle or centerFrames[1] == fpTitle)): -(extent.width div 2) + btnSize @@ -635,8 +668,12 @@ proc renderTop*(self: var Wm; client: var Client) = bsInactiveHover else: buttonStateOrig + if not fileExists self.config.closePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.closePaths[buttonState]: continue + + discard self.dpy.XMapWindow client.frame.close let image = self.XCreateImage(self.config.closePaths[buttonState], fp, attr) @@ -676,14 +713,21 @@ proc renderTop*(self: var Wm; client: var Client) = btnYOffset ) - discard self.dpy.XMapWindow client.frame.close self.XPutImage(image, client.frame.close, gc) of fpMaximize: + buttonState = if client.frame.maximizeHovered and buttonStateOrig == bsActive: + bsActiveHover + elif client.frame.maximizeHovered and buttonStateOrig == bsInactive: + bsInactiveHover + else: + buttonStateOrig + if not fileExists self.config.maximizePaths[buttonState]: - continue + buttonState = buttonStateOrig + if not fileExists self.config.maximizePaths[buttonState]: continue discard self.dpy.XMapWindow client.frame.maximize @@ -732,10 +776,16 @@ proc renderTop*(self: var Wm; client: var Client) = self.XPutImage(image, client.frame.maximize, gc) of fpMinimize: - if not fileExists self.config.minimizePaths[buttonState]: - continue + buttonState = if client.frame.minimizeHovered and buttonStateOrig == bsActive: + bsActiveHover + elif client.frame.minimizeHovered and buttonStateOrig == bsInactive: + bsInactiveHover + else: + buttonStateOrig - discard self.dpy.XMapWindow client.frame.minimize + if not fileExists self.config.minimizePaths[buttonState]: + buttonState = buttonStateOrig + if not fileExists self.config.minimizePaths[buttonState]: continue let rightFrames = self.config.frameParts.right @@ -777,6 +827,8 @@ proc renderTop*(self: var Wm; client: var Client) = self.config.buttonOffset.y.cint ) + discard self.dpy.XMapWindow client.frame.minimize + let image = self.XCreateImage(self.config.minimizePaths[buttonState], fp, attr) self.XPutImage(image, client.frame.minimize, gc) diff --git a/src/wormc.nim b/src/wormc.nim index 09c9d82..c81f43e 100644 --- a/src/wormc.nim +++ b/src/wormc.nim @@ -99,18 +99,36 @@ proc main() = of "close-inactive-path": dpy.sendStrPrep(ipcAtoms[IpcCloseInactivePath], params[i+1]) data = ipcAtoms[IpcCloseInactivePath].formatMess() + of "close-active-hovered-path": + dpy.sendStrPrep(ipcAtoms[IpcCloseActiveHoveredPath], params[i+1]) + data = ipcAtoms[IpcCloseActiveHoveredPath].formatMess() + of "close-inactive-hovered-path": + dpy.sendStrPrep(ipcAtoms[IpcCloseInactiveHoveredPath], params[i+1]) + data = ipcAtoms[IpcCloseInactiveHoveredPath].formatMess() of "maximize-active-path": dpy.sendStrPrep(ipcAtoms[IpcMaximizeActivePath], params[i+1]) data = ipcAtoms[IpcMaximizeActivePath].formatMess() of "maximize-inactive-path": dpy.sendStrPrep(ipcAtoms[IpcMaximizeInactivePath], params[i+1]) data = ipcAtoms[IpcMaximizeInactivePath].formatMess() + of "maximize-active-hovered-path": + dpy.sendStrPrep(ipcAtoms[IpcMaximizeActiveHoveredPath], params[i+1]) + data = ipcAtoms[IpcMaximizeActiveHoveredPath].formatMess() + of "maximize-inactive-hovered-path": + dpy.sendStrPrep(ipcAtoms[IpcMaximizeInactiveHoveredPath], params[i+1]) + data = ipcAtoms[IpcMaximizeInactiveHoveredPath].formatMess() of "minimize-active-path": dpy.sendStrPrep(ipcAtoms[IpcMinimizeActivePath], params[i+1]) data = ipcAtoms[IpcMinimizeActivePath].formatMess() of "minimize-inactive-path": dpy.sendStrPrep(ipcAtoms[IpcMinimizeInactivePath], params[i+1]) data = ipcAtoms[IpcMinimizeInactivePath].formatMess() + of "minimize-active-hovered-path": + dpy.sendStrPrep(ipcAtoms[IpcMinimizeActiveHoveredPath], params[i+1]) + data = ipcAtoms[IpcMinimizeActiveHoveredPath].formatMess() + of "minimize-inactive-hovered-path": + dpy.sendStrPrep(ipcAtoms[IpcMinimizeInactiveHoveredPath], params[i+1]) + data = ipcAtoms[IpcMinimizeInactiveHoveredPath].formatMess() of "decoration-disable": dpy.sendStrPrep(ipcAtoms[IpcDecorationDisable], params[i+1]) data = ipcAtoms[IpcDecorationDisable].formatMess()