From 71f4a1f97be1341ad06495fb395ed6320cdf799c Mon Sep 17 00:00:00 2001 From: eqf0 Date: Mon, 7 Feb 2022 22:55:29 -0600 Subject: [PATCH 1/3] Storing atoms strings inside their definition Enumerations can hold both a number and a string representation. This simplifies a little bit the procedures to get the atoms --- src/atoms.nim | 166 +++++++++++++++++++++++--------------------------- 1 file changed, 75 insertions(+), 91 deletions(-) diff --git a/src/atoms.nim b/src/atoms.nim index 8d6229b..abe9aca 100644 --- a/src/atoms.nim +++ b/src/atoms.nim @@ -1,100 +1,84 @@ -import x11/[xlib, x] +import + x11/[xlib, x ] + converter toXBool(x: bool): XBool = x.XBool converter toBool(x: XBool): bool = x.bool + type NetAtom* = enum - NetActiveWindow, NetSupported, - NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, - NetSystemTrayOrientationHorz, - NetWMName, NetWMState, NetWMStateAbove, NetWMStateMaximizedVert, NetWMStateMaximizedHorz, NetWMStateSticky, NetWMStateModal, - NetSupportingWMCheck, NetWMStateFullScreen, NetClientList, - NetWMStrutPartial, - NetWMWindowType, NetWMWindowTypeNormal, NetWMWindowTypeDialog, - NetWMWindowTypeUtility, - NetWMWindowTypeToolbar, NetWMWindowTypeSplash, NetWMWindowTypeMenu, - NetWMWindowTypeDropdownMenu, NetWMWindowTypePopupMenu, - NetWMWindowTypeTooltip, - NetWMWindowTypeNotification, NetWMWindowTypeDock, NetWMWindowTypeDesktop, - NetWMDesktop, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, - NetDesktopNames, NetFrameExtents + NetActiveWindow = "_NET_ACTIVE_WINDOW", + NetSupported = "_NET_SUPPORTED", + NetSystemTray = "_NET_SYSTEM_TRAY_S0", + NetSystemTrayOP = "_NET_SYSTEM_TRAY_OPCODE", + NetSystemTrayOrientation = "_NET_SYSTEM_TRAY_ORIENTATION", + NetSystemTrayOrientationHorz = "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", + NetWMName = "_NET_WM_NAME", + NetWMState = "_NET_WM_STATE", + NetWMStateAbove = "_NET_WM_STATE_ABOVE", + NetWMStateMaximizedVert = "_NET_WM_STATE_MAXIMIZED_VERT", + NetWMStateMaximizedHorz = "_NET_WM_STATE_MAXIMIZED_HORZ", + NetWMStateSticky = "_NET_WM_STATE_STICKY", + NetWMStateModal = "_NET_WM_STATE_MODAL", + NetSupportingWMCheck = "_NET_SUPPORTING_WM_CHECK", + NetWMStateFullScreen = "_NET_WM_STATE_FULLSCREEN", + NetClientList = "_NET_CLIENT_LIST", + NetWMStrutPartial = "_NET_WM_STRUT_PARTIAL", + NetWMWindowType = "_NET_WM_WINDOW_TYPE", + NetWMWindowTypeNormal = "_NET_WM_WINDOW_TYPE_NORMAL", + NetWMWindowTypeDialog = "_NET_WM_WINDOW_TYPE_DIALOG", + NetWMWindowTypeUtility = "_NET_WM_WINDOW_TYPE_UTILITY", + NetWMWindowTypeToolbar = "_NET_WM_WINDOW_TYPE_TOOLBAR", + NetWMWindowTypeSplash = "_NET_WM_WINDOW_TYPE_SPLASH", + NetWMWindowTypeMenu = "_NET_WM_WINDOW_TYPE_MENU", + NetWMWindowTypeDropdownMenu = "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", + NetWMWindowTypePopupMenu = "_NET_WM_WINDOW_TYPE_POPUP_MENU", + NetWMWindowTypeTooltip = "_NET_WM_WINDOW_TYPE_TOOLTIP", + NetWMWindowTypeNotification = "_NET_WM_WINDOW_TYPE_NOTIFICATION", + NetWMWindowTypeDock = "_NET_WM_WINDOW_TYPE_DOCK", + NetWMWindowTypeDesktop = "_NET_WM_WINDOW_TYPE_DESKTOP", + NetWMDesktop = "_NET_WM_DESKTOP", + NetDesktopViewport = "_NET_DESKTOP_VIEWPORT", + NetNumberOfDesktops = "_NET_NUMBER_OF_DESKTOPS", + NetCurrentDesktop = "_NET_CURRENT_DESKTOP", + NetDesktopNames = "_NET_DESKTOP_NAMES", + NetFrameExtents = "_NET_FRAME_EXTENTS" + IpcAtom* = enum - IpcClientMessage, IpcBorderActivePixel, IpcBorderInactivePixel, - IpcBorderWidth, IpcFrameActivePixel, IpcFrameInactivePixel, IpcFrameHeight, IpcTextActivePixel, IpcTextInactivePixel, - IpcTextFont, IpcTextOffset, IpcKillClient, IpcCloseClient, IpcSwitchTag, IpcLayout, IpcGaps, IpcMaster, IpcStruts, - IpcMoveTag, IpcFrameLeft, IpcFrameCenter, IpcFrameRight, - IpcFloat, IpcButtonOffset, IpcButtonSize, IpcRootMenu, IpcClosePath, IpcMaximizePath, IpcMaximizeClient, - IpcDecorationDisable + IpcClientMessage = "WORM_IPC_CLIENT_MESSAGE", + IpcBorderActivePixel = "WORM_IPC_BORDER_ACTIVE_PIXEL", + IpcBorderInactivePixel = "WORM_IPC_BORDER_INACTIVE_PIXEL", + IpcBorderWidth = "WORM_IPC_BORDER_WIDTH", + IpcFrameActivePixel = "WORM_IPC_FRAME_ACTIVE_PIXEL", + IpcFrameInactivePixel = "WORM_IPC_FRAME_INACTIVE_PIXEL", + IpcFrameHeight = "WORM_IPC_FRAME_HEIGHT", + IpcTextActivePixel = "WORM_IPC_TEXT_ACTIVE_PIXEL", + IpcTextInactivePixel = "WORM_IPC_TEXT_INACTIVE_PIXEL", + IpcTextFont = "WORM_IPC_TEXT_FONT", + IpcTextOffset = "WORM_IPC_TEXT_OFFSET", + IpcKillClient = "WORM_IPC_KILL_CLIENT", + IpcCloseClient = "WORM_IPC_CLOSE_CLIENT", + IpcSwitchTag = "WORM_IPC_SWITCH_TAG", + IpcLayout = "WORM_IPC_LAYOUT", + IpcGaps = "WORM_IPC_GAPS", + IpcMaster = "WORM_IPC_MASTER", + IpcStruts = "WORM_IPC_STRUTS", + IpcMoveTag = "WORM_IPC_MOVE_TAG", + IpcFrameLeft = "WORM_IPC_FRAME_LEFT", + IpcFrameCenter = "WORM_IPC_FRAME_CENTER", + IpcFrameRight = "WORM_IPC_FRAME_RIGHT", + IpcFloat = "WORM_IPC_FLOAT", + IpcButtonOffset = "WORM_IPC_BUTTON_OFFSET", + IpcButtonSize = "WORM_IPC_BUTTON_SIZE", + IpcRootMenu = "WORM_IPC_ROOT_MENU", + IpcClosePath = "WORM_IPC_CLOSE_PATH", + IpcMaximizePath = "WORM_IPC_MAXIMIZE_PATH", + IpcMaximizeClient = "WORM_IPC_MAXIMIZE_CLIENT", + IpcDecorationDisable = "WORM_IPC_DECORATION_DISABLE" func getNetAtoms*(dpy: ptr Display): array[NetAtom, Atom] = - [ - dpy.XInternAtom("_NET_ACTIVE_WINDOW", false), - dpy.XInternAtom("_NET_SUPPORTED", false), - dpy.XInternAtom("_NET_SYSTEM_TRAY_S0", false), - dpy.XInternAtom("_NET_SYSTEM_TRAY_OPCODE", false), - dpy.XInternAtom("_NET_SYSTEM_TRAY_ORIENTATION", false), - dpy.XInternAtom("_NET_SYSTEM_TRAY_ORIENTATION_HORZ", false), - dpy.XInternAtom("_NET_WM_NAME", false), - dpy.XInternAtom("_NET_WM_STATE", false), - dpy.XInternAtom("_NET_WM_STATE_ABOVE", false), - dpy.XInternAtom("_NET_WM_STATE_MAXIMIZED_VERT", false), - dpy.XInternAtom("_NET_WM_STATE_MAXIMIZED_HORZ", false), - dpy.XInternAtom("_NET_WM_STATE_STICKY", false), - dpy.XInternAtom("_NET_WM_STATE_MODAL", false), - dpy.XInternAtom("_NET_SUPPORTING_WM_CHECK", false), - dpy.XInternAtom("_NET_WM_STATE_FULLSCREEN", false), - dpy.XInternAtom("_NET_CLIENT_LIST", false), - dpy.XInternAtom("_NET_WM_STRUT_PARTIAL", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_NORMAL", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_DIALOG", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_UTILITY", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_TOOLBAR", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_SPLASH", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_MENU", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_POPUP_MENU", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_TOOLTIP", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_NOTIFICATION", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_DOCK", false), - dpy.XInternAtom("_NET_WM_WINDOW_TYPE_DESKTOP", false), - dpy.XInternAtom("_NET_WM_DESKTOP", false), - dpy.XInternAtom("_NET_DESKTOP_VIEWPORT", false), - dpy.XInternAtom("_NET_NUMBER_OF_DESKTOPS", false), - dpy.XInternAtom("_NET_CURRENT_DESKTOP", false), - dpy.XInternAtom("_NET_DESKTOP_NAMES", false), - dpy.XInternAtom("_NET_FRAME_EXTENTS", false) - ] + for atom in NetAtom: + result[atom] = dpy.XInternAtom(($atom).cstring, false) func getIpcAtoms*(dpy: ptr Display): array[IpcAtom, Atom] = - [ - dpy.XInternAtom("WORM_IPC_CLIENT_MESSAGE", false), - dpy.XInternAtom("WORM_IPC_BORDER_ACTIVE_PIXEL", false), - dpy.XInternAtom("WORM_IPC_BORDER_INACTIVE_PIXEL", false), - dpy.XInternAtom("WORM_IPC_BORDER_WIDTH", false), - dpy.XInternAtom("WORM_IPC_FRAME_ACTIVE_PIXEL", false), - dpy.XInternAtom("WORM_IPC_FRAME_INACTIVE_PIXEL", false), - dpy.XInternAtom("WORM_IPC_FRAME_HEIGHT", false), - dpy.XInternAtom("WORM_IPC_TEXT_ACTIVE_PIXEL", false), - dpy.XInternAtom("WORM_IPC_TEXT_INACTIVE_PIXEL", false), - dpy.XInternAtom("WORM_IPC_TEXT_FONT", false), - dpy.XInternAtom("WORM_IPC_TEXT_OFFSET", false), - dpy.XInternAtom("WORM_IPC_KILL_CLIENT", false), - dpy.XInternAtom("WORM_IPC_CLOSE_CLIENT", false), - dpy.XInternAtom("WORM_IPC_SWITCH_TAG", false), - dpy.XInternAtom("WORM_IPC_LAYOUT", false), - dpy.XInternAtom("WORM_IPC_MASTER", false), - dpy.XInternAtom("WORM_IPC_GAPS", false), - dpy.XInternAtom("WORM_IPC_STRUTS", false), - dpy.XInternAtom("WORM_IPC_MOVE_TAG", false), - dpy.XInternAtom("WORM_IPC_FRAME_LEFT", false), - dpy.XInternAtom("WORM_IPC_FRAME_CENTER", false), - dpy.XInternAtom("WORM_IPC_FRAME_RIGHT", false), - dpy.XInternAtom("WORM_IPC_FLOAT", false), - dpy.XInternAtom("WORM_IPC_BUTTON_OFFSET", false), - dpy.XInternAtom("WORM_IPC_BUTTON_SIZE", false), - dpy.XInternAtom("WORM_IPC_ROOT_MENU", false), - dpy.XInternAtom("WORM_IPC_CLOSE_PATH", false), - dpy.XInternAtom("WORM_IPC_MAXIMIZE_PATH", false), - dpy.XInternAtom("WORM_IPC_MAXIMIZE_CLIENT", false), - dpy.XInternAtom("WORM_IPC_DECORATION_DISABLE", false) - ] + for atom in IpcAtom: + result[atom] = dpy.XInternAtom(($atom).cstring, false) From 5cecd067d6bd63f51469a21c1bbc66e9a78d88b3 Mon Sep 17 00:00:00 2001 From: eqf0 Date: Mon, 7 Feb 2022 22:59:47 -0600 Subject: [PATCH 2/3] Removing forward declarations Reordered the code a little bit to prevent circular dependencies after deleting the forward declarations. This resulted in some procedures now living in `events.nim`. --- src/events.nim | 43 ++++++++++++++++++++++++++++++ src/wm.nim | 72 ++++++++++++++++++++++++-------------------------- src/worm.nim | 7 +++-- 3 files changed, 83 insertions(+), 39 deletions(-) create mode 100644 src/events.nim diff --git a/src/events.nim b/src/events.nim new file mode 100644 index 0000000..b29a2a4 --- /dev/null +++ b/src/events.nim @@ -0,0 +1,43 @@ +import + std/[os, osproc], + x11/[x, xlib], + wm, + log, + events/[ + buttonpress, + buttonrelease, + clientmessage, + configurenotify, + configurerequest, + destroynotify, + expose, + maprequest, + motionnotify, + propertynotify, + unmapnotify + ] + +proc dispatchEvent*(self: var Wm; ev: XEvent) = + case ev.theType: + of ButtonPress: self.handleButtonPress ev.xbutton + of ButtonRelease: self.handleButtonRelease ev.xbutton + of MotionNotify: self.handleMotionNotify ev.xmotion + of MapRequest: self.handleMapRequest ev.xmaprequest + of ConfigureRequest: self.handleConfigureRequest ev.xconfigurerequest + of ConfigureNotify: self.handleConfigureNotify ev.xconfigure + of UnmapNotify: self.handleUnmapNotify ev.xunmap + of DestroyNotify: self.handleDestroyNotify ev.xdestroywindow + of ClientMessage: self.handleClientMessage ev.xclient + of Expose: self.handleExpose ev.xexpose + of PropertyNotify: self.handlePropertyNotify ev.xproperty + else: discard + +proc eventLoop*(self: var Wm) = + if fileExists expandTilde "~/.config/worm/rc": + log "config file found, loading..." + discard startProcess expandTilde "~/.config/worm/rc" + log "config file loaded!" + while true: + discard self.dpy.XNextEvent(unsafeAddr self.currEv) + self.dispatchEvent self.currEv + diff --git a/src/wm.nim b/src/wm.nim index d4b1d95..3e2d16e 100644 --- a/src/wm.nim +++ b/src/wm.nim @@ -25,48 +25,46 @@ type tags*: TagSet layout*: Layout noDecorList*: seq[Regex] - # ignoreNextConfigureNotify*: bool - -# event handlers -# proc handleButtonPress(self: var Wm; ev: XButtonEvent): void -# proc handleButtonRelease(self: var Wm; ev: XButtonEvent): void -# proc handleMotionNotify(self: var Wm; ev: XMotionEvent): void -# proc handleMapRequest(self: var Wm; ev: XMapRequestEvent): void -# proc handleConfigureRequest(self: var Wm; ev: XConfigureRequestEvent): void -# proc handleUnmapNotify(self: var Wm; ev: XUnmapEvent): void -# proc handleDestroyNotify(self: var Wm; ev: XDestroyWindowEvent): void -# proc handleClientMessage(self: var Wm; ev: XClientMessageEvent): void -# proc handleConfigureNotify(self: var Wm; ev: XConfigureEvent): void -# proc handleExpose(self: var Wm; ev: XExposeEvent): void -# proc handlePropertyNotify(self: var Wm; ev: XPropertyEvent): void -# others -proc newWm*: Wm -proc tileWindows*(self: var Wm): void -proc renderTop*(self: var Wm; client: var Client): void -proc maximizeClient*(self: var Wm; client: var Client, force: bool = false, forceun: bool = false): void -proc eventLoop*(self: var Wm): void -proc dispatchEvent*(self: var Wm; ev: XEvent): void -func findClient*(self: var Wm; predicate: proc(client: Client): bool): Option[( - ptr Client, uint)] -proc updateClientList*(self: Wm): void -proc updateTagState*(self: Wm): void -import events/[buttonpress, buttonrelease, clientmessage, configurenotify, configurerequest, destroynotify, expose, maprequest, motionnotify, propertynotify, unmapnotify] -proc newWm*: Wm = + +proc initWm*(): Wm = let dpy = XOpenDisplay nil - if dpy == nil: quit 1 + + if dpy == nil: + quit 1 + log "Opened display" + let root = XDefaultRootWindow dpy + for button in [1'u8, 3]: # list from sxhkd (Mod2Mask NumLock, Mod3Mask ScrollLock, LockMask CapsLock). - for mask in [uint32 Mod1Mask, Mod1Mask or Mod2Mask, Mod1Mask or LockMask, - Mod1Mask or Mod3Mask, Mod1Mask or Mod2Mask or LockMask, Mod1Mask or - LockMask or Mod3Mask, Mod1Mask or Mod2Mask or Mod3Mask, Mod1Mask or - Mod2Mask or LockMask or Mod3Mask]: - discard dpy.XGrabButton(button, mask, root, true, ButtonPressMask or - PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None) - discard dpy.XSelectInput(root, SubstructureRedirectMask or SubstructureNotifyMask or ButtonPressMask) - let font = dpy.XftFontOpenName(XDefaultScreen dpy, "Noto Sans Mono:size=11") - let netAtoms = getNetAtoms dpy + for mask in [ + uint32 Mod1Mask, Mod1Mask or Mod2Mask, Mod1Mask or LockMask, + Mod1Mask or Mod3Mask, Mod1Mask or Mod2Mask or LockMask, Mod1Mask or + LockMask or Mod3Mask, Mod1Mask or Mod2Mask or Mod3Mask, Mod1Mask or + Mod2Mask or LockMask or Mod3Mask + ]: + discard dpy.XGrabButton( + button, + mask, + root, + true, + ButtonPressMask or PointerMotionMask, + GrabModeAsync, + GrabModeAsync, + None, + None + ) + + discard dpy.XSelectInput( + root, + SubstructureRedirectMask or SubstructureNotifyMask or ButtonPressMask + ) + + let + font = dpy.XftFontOpenName(XDefaultScreen dpy, "Noto Sans Mono:size=11") + netAtoms = getNetAtoms dpy + discard dpy.XChangeProperty( root, netAtoms[NetSupportingWMCheck], diff --git a/src/worm.nim b/src/worm.nim index 8057350..f37b092 100644 --- a/src/worm.nim +++ b/src/worm.nim @@ -1,4 +1,7 @@ -import wm +import + wm, + events + when isMainModule: - var instance = newWm() + var instance = initWm() instance.eventLoop() From afb3b00942e646155b18b58322c018bbe65d5cb3 Mon Sep 17 00:00:00 2001 From: eqf0 Date: Mon, 7 Feb 2022 23:01:08 -0600 Subject: [PATCH 3/3] Some code refactoring Just looked for repeated patterns across the code and moved some of it into separate procedures. --- src/wm.nim | 1201 +++++++++++++++++++++++++++++++------------------ src/wormc.nim | 273 +++++------ 2 files changed, 901 insertions(+), 573 deletions(-) diff --git a/src/wm.nim b/src/wm.nim index 3e2d16e..bef2626 100644 --- a/src/wm.nim +++ b/src/wm.nim @@ -1,11 +1,11 @@ -import std/[options, os, osproc, sequtils] -import x11/[xlib, x, xft, xatom, xinerama, xrender] -import types -import atoms -import log -import pixie -import regex -# import events/configurerequest +import + std/[options, os, sequtils], + x11/[xlib, x, xft, xatom, xinerama, xrender], + types, + atoms, + log, + pixie, + regex converter toXBool*(x: bool): XBool = x.XBool converter toBool*(x: XBool): bool = x.bool @@ -74,6 +74,7 @@ proc initWm*(): Wm = cast[cstring](unsafeAddr root), 1 ) + discard dpy.XChangeProperty( root, netAtoms[NetSupported], @@ -83,6 +84,7 @@ proc initWm*(): Wm = cast[cstring](unsafeAddr netAtoms), netAtoms.len.cint ) + let wmname = "worm".cstring discard dpy.XChangeProperty( root, @@ -93,6 +95,7 @@ proc initWm*(): Wm = wmname, 4 ) + var numdesk = [9] discard dpy.XChangeProperty( root, @@ -103,6 +106,7 @@ proc initWm*(): Wm = cast[cstring](addr numdesk), 1 ) + numdesk = [0] discard dpy.XChangeProperty( root, @@ -113,6 +117,7 @@ proc initWm*(): Wm = cast[cstring](addr numdesk), 1 ) + discard dpy.XChangeProperty( root, netAtoms[NetClientList], @@ -122,507 +127,830 @@ proc initWm*(): Wm = nil, 0 ) - discard XSetErrorHandler proc(dpy: ptr Display; - err: ptr XErrorEvent): cint {.cdecl.} = 0 + + discard XSetErrorHandler( + proc(dpy: ptr Display; err: ptr XErrorEvent): cint {.cdecl.} = 0 + ) + discard dpy.XSync false + discard dpy.XFlush - Wm(dpy: dpy, root: root, motionInfo: none MotionInfo, font: font, - netAtoms: netAtoms, ipcAtoms: getIpcAtoms dpy, config: Config( - borderActivePixel: 0x7499CC, borderInactivePixel: 0x000000, - borderWidth: 1, - frameActivePixel: 0x161821, frameInactivePixel: 0x666666, frameHeight: 30, - textActivePixel: 0xffffff, textInactivePixel: 0x000000, textOffset: (x: uint 10, y: uint 20), gaps: 0, buttonSize: 14, - struts: (top: uint 10, bottom: uint 40, left: uint 10, - right: uint 10)), tags: defaultTagSet(), - layout: lyFloating, noDecorList: @[]) # The default configuration is reasonably sane, and for now based on the Iceberg colorscheme. It may be changed later; it's recommended for users to write their own. - -func findClient*(self: var Wm; predicate: proc(client: Client): bool): Option[( - ptr Client, uint)] = + + # The default configuration is reasonably sane, and for now based on the + # Iceberg colorscheme. It may be changed later; it's recommended for users to + # write their own. + Wm( + dpy: dpy, + root: root, + motionInfo: none MotionInfo, + font: font, + netAtoms: netAtoms, + ipcAtoms: getIpcAtoms dpy, + config: Config( + borderActivePixel: 0x7499CC, + borderInactivePixel: 0x000000, + borderWidth: 1, + frameActivePixel: 0x161821, + frameInactivePixel: 0x666666, + frameHeight: 30, + textActivePixel: 0xffffff, + textInactivePixel: 0x000000, + textOffset: (x: uint 10, y: uint 20), + gaps: 0, + buttonSize: 14, + struts: (top: uint 10, bottom: uint 40, left: uint 10, right: uint 10) + ), + tags: defaultTagSet(), + layout: lyFloating, + noDecorList: @[] + ) + +func findClient*( + self: var Wm; + predicate: proc(client: Client): bool + ): Option[(ptr Client, uint)] = + for i, client in self.clients: if predicate client: return some((addr self.clients[i], uint i)) - return none((ptr Client, uint)) -proc tileWindows*(self: var Wm): void = - log "Tiling windows" - var clientLen: uint = 0 - var master: ptr Client = nil - for i, client in self.clients: - if client.fullscreen: return # causes issues - if client.tags == self.tags and not client.floating: # We only care about clients on the current tag. - if master == nil: # This must be the first client on the tag, otherwise master would not be nil; therefore, we promote it to master. - master = addr self.clients[i] - inc clientLen - if master == nil: return - if clientLen == 0: return # we got nothing to tile. - var scrNo: cint - var scrInfo = cast[ptr UncheckedArray[XineramaScreenInfo]]( - self.dpy.XineramaQueryScreens(addr scrNo)) - # echo cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - let masterWidth = if clientLen == 1: - uint scrInfo[0].width - self.config.struts.left.cint - - self.config.struts.right.cint - cint self.config.borderWidth*2 - else: - uint scrInfo[0].width shr 1 - self.config.struts.left.cint - - cint self.config.borderWidth*2 - log $masterWidth - discard self.dpy.XMoveResizeWindow(master.frame.window, - cint self.config.struts.left, cint self.config.struts.top, - cuint masterWidth, cuint scrInfo[0].height - - self.config.struts.top.int16 - self.config.struts.bottom.int16 - - cint self.config.borderWidth*2) - discard self.dpy.XResizeWindow(master.window, cuint masterWidth, - cuint scrInfo[0].height - self.config.struts.top.cint - - self.config.struts.bottom.cint - master.frameHeight.cint - - cint self.config.borderWidth*2) - for win in [master.frame.title, master.frame.top]: discard self.dpy.XResizeWindow(win, cuint masterWidth, cuint master.frameHeight) - self.renderTop master[] - # discard self.dpy.XMoveResizeWindow(master.frame.window, cint self.config.struts.left, cint self.config.struts.top, cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth * 2) - self.config.gaps*2 - int16 self.config.struts.right, cuint scrInfo[0].height - int16(self.config.borderWidth * 2) - int16(self.config.struts.top) - int16(self.config.struts.bottom)) # bring the master window up to cover half the screen - # discard self.dpy.XResizeWindow(master.window, cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth*2) - self.config.gaps*2 - int16 self.config.struts.right, cuint scrInfo[0].height - int16(self.config.borderWidth*2) - int16(self.config.frameHeight) - int16(self.config.struts.top) - int16(self.config.struts.bottom)) # bring the master window up to cover half the screen - var irrevelantLen: uint = 0 - for i, client in self.clients: - if client.tags != self.tags or client == master[] or client.floating: - inc irrevelantLen - continue - if clientLen == 2: - discard self.dpy.XMoveWindow(client.frame.window, cint scrInfo[ - 0].width shr 1 + self.config.gaps, cint self.config.struts.top) - let w = cuint scrInfo[0].width shr ( - if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth*2) - - self.config.gaps - self.config.struts.right.cint - discard self.dpy.XResizeWindow(client.frame.top, w, cuint self.config.frameHeight) - discard self.dpy.XResizeWindow(client.frame.title, w, cuint self.config.frameHeight) - discard self.dpy.XResizeWindow(client.window, w, cuint scrInfo[ - 0].height - self.config.struts.top.cint - - self.config.struts.bottom.cint - client.frameHeight.cint - - cint self.config.borderWidth*2) - else: - let stackElem = i - int irrevelantLen - - 1 # How many windows are there in the stack? We must subtract 1 to ignore the master window; which we iterate over too. - let yGap = if stackElem != 0: - self.config.gaps - else: - 0 - # let subStrut = if stackElem = clientLen - # XXX: the if stackElem == 1: 0 else: self.config.gaps is a huge hack - # and also incorrect behavior; while usually un-noticeable it makes the top window in the stack bigger by the gaps. Fix this!! - let w = cuint scrInfo[0].width shr ( - if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth*2) - - self.config.gaps - self.config.struts.right.cint - discard self.dpy.XResizeWindow(client.frame.top, w, cuint self.config.frameHeight) - discard self.dpy.XResizeWindow(client.frame.title, w, cuint self.config.frameHeight) - discard self.dpy.XMoveWindow(client.frame.window, cint scrInfo[ - 0].width shr 1 + yGap, cint((float(scrInfo[0].height) - ( - self.config.struts.bottom.float + self.config.struts.top.float)) * (( - i - int irrevelantLen) / int clientLen - 1)) + - self.config.struts.top.cint + (if stackElem == - 1: 0 else: self.config.gaps.cint)) - discard self.dpy.XResizeWindow(client.window, w, cuint ((scrInfo[ - 0].height - self.config.struts.bottom.cint - - self.config.struts.top.cint) div int16(clientLen - 1)) - int16( - self.config.borderWidth*2) - int16(client.frameHeight) - ( - if stackElem == 1: 0 else: self.config.gaps)) - # the number of windows on the stack is i (the current client) minus the master window minus any irrevelant windows - # discard self.dpy.XMoveResizeWindow(client.frame.window, cint scrInfo[0].width shr 1, cint(float(scrInfo[0].height) * ((i - int irrevelantLen) / int clientLen - 1)) + cint self.config.gaps, cuint scrInfo[0].width shr 1 - int16(self.config.borderWidth * 2) - self.config.gaps, cuint (scrInfo[0].height div int16(clientLen - 1)) - int16(self.config.struts.bottom) - int16(self.config.borderWidth * 2) - self.config.gaps) # bring the master window up to cover half the screen - # discard self.dpy.XResizeWindow(client.window, cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth*2) - self.config.gaps, cuint (scrInfo[0].height div int16(clientLen - 1)) - int16(self.config.struts.bottom) - int16(self.config.borderWidth*2) - int16(self.config.frameHeight) - self.config.gaps) # bring the master window up to cover half the screen - self.renderTop self.clients[i] - discard self.dpy.XSync false - discard self.dpy.XFlush +proc createBtnImg(c: Config, imgPath: string, framePixel: uint): Image = + let btnSize = c.buttonSize.int + result = newImage(btnSize, btnSize) + + let buttonColor = cast[array[3, uint8]](framePixel) + + result.fill(rgba(buttonColor[2], buttonColor[1], buttonColor[0], 255)) + + let img = readImage(imgPath) + result.draw( + img, + scale(vec2(btnSize / img.width, btnSize / img.height)) + ) + +proc getBGRXBitmap(im: Image): seq[ColorRGBX] = + var ctx = newContext im + # convert to BGRA + result = ctx.image.data + + for i, color in result: + let x = color + # RGBX -> BGRX + result[i].r = x.b + result[i].b = x.r -proc renderTop*(self: var Wm; client: var Client): void = +proc XCreateImage( + self: var Wm, + imgPath: string, + framePixel: uint, + attr: XWindowAttributes + ): PXImage = + var screen = self.config.createBtnImg(imgPath, framePixel) + + log $attr.depth + + var + bitmap = screen.getBGRXBitmap() + frameBuffer = addr bitmap[0] + + result = XCreateImage( + self.dpy, + attr.visual, + attr.depth.cuint, + ZPixmap, + 0, + cast[cstring](frameBuffer), + self.config.buttonSize.cuint, + self.config.buttonSize.cuint, + 8, + self.config.buttonSize.cint*4 + ) + +proc XPutImage(self: var Wm, img: PXImage, win: Window, gc: GC) = + let btnSize = self.config.buttonSize.cuint + discard self.dpy.XPutImage(win, gc, img, 0, 0, 0, 0, btnSize, btnSize) + +proc renderTop*(self: var Wm; client: var Client) = var extent: XGlyphInfo - self.dpy.XftTextExtentsUtf8(self.font, cast[ptr char](cstring client.title), - cint client.title.len, addr extent) + self.dpy.XftTextExtentsUtf8( + self.font, + cast[ptr char](cstring client.title), + cint client.title.len, addr extent + ) + var attr: XWindowAttributes discard self.dpy.XGetWindowAttributes(client.frame.window, addr attr) - for win in [client.frame.title, client.frame.close, client.frame.maximize]: discard self.dpy.XClearWindow win - var gc: GC - var gcVal: XGCValues - gc = self.dpy.XCreateGC(client.frame.close, 0, addr gcVal) + + for win in [client.frame.title, client.frame.close, client.frame.maximize]: + discard self.dpy.XClearWindow win + + var + gcVal: XGCValues + gc = self.dpy.XCreateGC(client.frame.close, 0, addr gcVal) + # discard self.dpy.XSetForeground(gc, self.config.textActivePixel) - let fp = if self.focused.isSome and client == self.clients[self.focused.get]: self.config.frameActivePixel else: self.config.frameInactivePixel + let fp = + if self.focused.isSome and client == self.clients[self.focused.get]: + self.config.frameActivePixel + else: + self.config.frameInactivePixel + # draw the 3 'regions' of the titlebar; left, center, right - var closeExists = false - var maximizeExists = false + var + closeExists = false + maximizeExists = false + discard self.dpy.XUnmapWindow client.frame.close discard self.dpy.XUnmapWindow client.frame.maximize + # load the image @ path into the frame top at offset (x, y) proc loadImage(path: string; x, y: uint): void = discard + for i, part in self.config.frameParts.left: case part: of fpTitle: - if not closeExists: discard self.dpy.XUnmapWindow client.frame.close - if not maximizeExists: discard self.dpy.XUnmapWindow client.frame.maximize - client.draw.XftDrawStringUtf8(addr client.color, self.font, - self.config.textOffset.x.cint + ( - if i == 1 and self.config.frameParts.left[0] in {fpClose, fpMaximize}: - self.config.buttonSize.cint + self.config.buttonOffset.x.cint + + if not closeExists: + discard self.dpy.XUnmapWindow client.frame.close + + if not maximizeExists: + discard self.dpy.XUnmapWindow client.frame.maximize + + let + buttonSize = self.config.buttonSize.cint + buttonXOffset = self.config.buttonOffset.x.cint + leftFrame0 = self.config.frameParts.left[0] + + offset = + if i == 1 and leftFrame0 in {fpClose, fpMaximize}: + buttonSize + buttonXOffset elif i == 2: - self.config.buttonSize.cint*2 + self.config.buttonOffset.x.cint*2 - else: 0), - cint self.config.textOffset.y, cast[ - ptr char](cstring client.title), cint client.title.len) + (buttonSize + buttonXOffset) * 2 + else: + 0 + + client.draw.XftDrawStringUtf8( + addr client.color, + self.font, + self.config.textOffset.x.cint + offset, + self.config.textOffset.y.cint, + cast[ptr char](cstring client.title), + client.title.len.cint + ) + of fpClose: + closeExists = true - if not fileExists self.config.closePath: continue + + if not fileExists self.config.closePath: + continue + discard self.dpy.XMapWindow client.frame.close - discard self.dpy.XMoveWindow(client.frame.close, - self.config.buttonOffset.x.cint + ( - if i == 1 and self.config.frameParts.left[0] == fpTitle: extent.width + - self.config.textOffset.x.cint - elif i == 1 and self.config.frameParts.left[0] == fpMaximize: - self.config.buttonSize.cint + self.config.buttonOffset.x.cint - elif i == 2: - extent.width + self.config.textOffset.x.cint + self.config.buttonOffset.x.cint + self.config.buttonSize.cint - else: 0), self.config.buttonOffset.y.cint) - var - screen = newImage(self.config.buttonSize.int, self.config.buttonSize.int) - let buttonColor = cast[array[3, uint8]](fp) - screen.fill(rgba(buttonColor[2],buttonColor[1],buttonColor[0],255)) - let img = readImage(self.config.closePath) - screen.draw( - img, - # translate(vec2(100, 100)) * - scale(vec2(self.config.buttonSize.int / img.width, self.config.buttonSize.int / img.height)) - # translate(vec2(-450, -450)) + + let + buttonSize = self.config.buttonSize.cint + buttonXOffset = self.config.buttonOffset.x.cint + buttonYOffset = self.config.buttonOffset.y.cint + textXOffset = self.config.textOffset.x.cint + leftFrame0 = self.config.frameParts.left[0] + + offset = + if i == 1 and leftFrame0 == fpTitle: + extent.width + textXOffset + elif i == 1 and leftFrame0 == fpMaximize: + buttonSize + buttonXOffset + elif i == 2: + extent.width + textXOffset + buttonXOffset + buttonSize + else: + 0 + + discard self.dpy.XMoveWindow( + client.frame.close, + buttonXOffset + offset, + buttonYOffset ) - log $attr.depth - var ctx = newContext screen - # convert to BGRA - var frameBufferEndian = ctx.image.data - for i, color in frameBufferEndian: - let x = color - # RGBX -> BGRX - frameBufferEndian[i].r = x.b - frameBufferEndian[i].b = x.r - var frameBuffer = addr frameBufferEndian[0] - let image = XCreateImage(self.dpy, attr.visual, attr.depth.cuint, ZPixmap, 0, cast[cstring]( - frameBuffer), self.config.buttonSize.cuint, self.config.buttonSize.cuint, 8, cint(self.config.buttonSize*4)) - discard XPutImage(self.dpy, client.frame.close, gc, image, 0, 0, 0, 0, self.config.buttonSize.cuint, self.config.buttonSize.cuint) + + let image = self.XCreateImage(self.config.closePath, fp, attr) + + self.XPutImage(image, client.frame.close, gc) + of fpMaximize: + maximizeExists = true - if not fileExists self.config.maximizePath: continue + + if not fileExists self.config.maximizePath: + continue + discard self.dpy.XMapWindow client.frame.maximize - discard self.dpy.XMoveWindow(client.frame.maximize, - self.config.buttonOffset.x.cint + ( - if i == 1 and self.config.frameParts.left[0] == fpTitle: - extent.width.cint - elif i == 1 and self.config.frameParts.left[0] == fpClose: - self.config.buttonSize.cint + self.config.buttonOffset.x.cint - elif i == 2: - extent.width + self.config.buttonOffset.x.cint + self.config.buttonSize.cint - else: 0), self.config.buttonOffset.y.cint) - var - screen = newImage(self.config.buttonSize.int, self.config.buttonSize.int) - let buttonColor = cast[array[3, uint8]](fp) - screen.fill(rgba(buttonColor[2],buttonColor[1],buttonColor[0],255)) - let img = readImage(self.config.maximizePath) - screen.draw( - img, - # translate(vec2(100, 100)) * - scale(vec2(self.config.buttonSize.int / img.width, self.config.buttonSize.int / img.height)) - # translate(vec2(-450, -450)) + + let + leftFrame0 = self.config.frameParts.left[0] + btnSize = self.config.buttonSize.cint + btnXOffset = self.config.buttonOffset.x.cint + + offset = + if i == 1 and leftFrame0 == fpTitle: + extent.width.cint + elif i == 1 and leftFrame0 == fpClose: + btnSize + btnXOffset + elif i == 2: + extent.width + btnXOffset + btnSize + else: + 0 + + discard self.dpy.XMoveWindow( + client.frame.maximize, + self.config.buttonOffset.x.cint + offset, + self.config.buttonOffset.y.cint ) - log $attr.depth - var ctx = newContext screen - # convert to BGRA - var frameBufferEndian = ctx.image.data - for i, color in frameBufferEndian: - let x = color - # RGBX -> BGRX - frameBufferEndian[i].r = x.b - frameBufferEndian[i].b = x.r - var frameBuffer = addr frameBufferEndian[0] - let image = XCreateImage(self.dpy, attr.visual, attr.depth.cuint, ZPixmap, 0, cast[cstring]( - frameBuffer), self.config.buttonSize.cuint, self.config.buttonSize.cuint, 8, cint(self.config.buttonSize*4)) - discard XPutImage(self.dpy, client.frame.maximize, gc, image, 0, 0, 0, 0, self.config.buttonSize.cuint, self.config.buttonSize.cuint) + + let image = self.XCreateImage(self.config.maximizePath, fp, attr) + + self.XPutImage(image, client.frame.maximize, gc) + for i, part in self.config.frameParts.center: case part: of fpTitle: - if not closeExists: discard self.dpy.XUnmapWindow client.frame.close - client.draw.XftDrawStringUtf8(addr client.color, self.font, - (cint(attr.width div 2) - cint (extent.width div 2)) + (if i == 2: self.config.buttonSize.cint else: 0) + self.config.textOffset.x.cint, - cint self.config.textOffset.y, cast[ - ptr char](cstring client.title), cint client.title.len) + + if not closeExists: + discard self.dpy.XUnmapWindow client.frame.close + + let configButtonSize = + if i == 2: + self.config.buttonSize.cint + else: + 0 + + client.draw.XftDrawStringUtf8( + addr client.color, + self.font, + ((attr.width div 2).cint - (extent.width.cint div 2)) + + configButtonSize + self.config.textOffset.x.cint, + self.config.textOffset.y.cint, + cast[ptr char](cstring client.title), + client.title.len.cint + ) + of fpClose: + closeExists = true - if not fileExists self.config.closePath: continue - var - screen = newImage(self.config.buttonSize.int, self.config.buttonSize.int) - let buttonColor = cast[array[3, uint8]](fp) - screen.fill(rgba(buttonColor[2],buttonColor[1],buttonColor[0],255)) - let img = readImage(self.config.closePath) - screen.draw( - img, - # translate(vec2(100, 100)) * - scale(vec2(self.config.buttonSize.int / img.width, self.config.buttonSize.int / img.height)) - # translate(vec2(-450, -450)) - ) - log $attr.depth - var ctx = newContext screen - # convert to BGRA - var frameBufferEndian = ctx.image.data - for i, color in frameBufferEndian: - let x = color - # RGBX -> BGRX - frameBufferEndian[i].r = x.b - frameBufferEndian[i].b = x.r - var frameBuffer = addr frameBufferEndian[0] - let image = XCreateImage(self.dpy, attr.visual, attr.depth.cuint, ZPixmap, 0, cast[cstring]( - frameBuffer), self.config.buttonSize.cuint, self.config.buttonSize.cuint, 8, cint(self.config.buttonSize*4)) - discard self.dpy.XMoveWindow(client.frame.close, (if i == - 0: -self.config.buttonOffset.x.cint else: self.config.buttonOffset.x.cint) + - (if i == 1 and self.config.frameParts.center[0] == fpTitle and self.config.frameParts.center.len == 2: - self.config.textOffset.x.cint + extent.width div 2 - elif i == 1 and self.config.frameParts.center[0] == fpTitle and self.config.frameParts.center.len > 2 and self.config.frameParts.center[1] == fpMaximize: - -(extent.width div 2) - self.config.buttonSize.cint - self.config.buttonOffset.x.cint - self.config.textOffset.x.cint - elif i == 2 and self.config.frameParts.center[0] == fpTitle: - (extent.width div 2) + self.config.buttonOffset.x.cint + self.config.textOffset.x.cint + self.config.buttonSize.cint - elif i == 1 and self.config.frameParts.center[0] == fpTitle: - (extent.width div 2) + self.config.buttonOffset.x.cint + self.config.textOffset.x.cint - self.config.buttonSize.cint - elif i == 1 and self.config.frameParts.center.len >= 3 and self.config.frameParts.center[0] == fpMaximize and self.config.frameParts.center[2] == fpTitle: - # meh + + if not fileExists self.config.closePath: + continue + + let image = self.XCreateImage(self.config.closePath, fp, attr) + + let + btnSize = self.config.buttonSize.cint + btnXOffset = self.config.buttonOffset.x.cint + textXOffset = self.config.textOffset.x.cint + centerFrames = self.config.frameParts.center + + discard self.dpy.XMoveWindow( + client.frame.close, + (if i == 0: -btnXOffset else: btnXOffset) + ( + if (i == 1 and centerFrames[0] == fpTitle and centerFrames.len == 2): + textXOffset + extent.width div 2 + elif (i == 1 and centerFrames[0] == fpTitle and + centerFrames.len > 2 and centerFrames[1] == fpMaximize): + -(extent.width div 2) - btnSize - btnXOffset - textXOffset + elif i == 2 and centerFrames[0] == fpTitle: + (extent.width div 2) + btnXOffset + textXOffset + btnSize + elif i == 1 and centerFrames[0] == fpTitle: + (extent.width div 2) + btnXOffset + textXOffset - btnSize + elif (i == 1 and centerFrames.len >= 3 and + centerFrames[0] == fpMaximize and centerFrames[2] == fpTitle): + # meh -(extent.width div 2) - elif i == 2 and self.config.frameParts.center[1] == fpTitle: - self.config.buttonSize.cint + extent.width div 2 - elif i == 1 and self.config.frameParts.center.len >= 3 and self.config.frameParts.center[2] == fpMaximize: + elif i == 2 and centerFrames[1] == fpTitle: + btnSize + extent.width div 2 + elif (i == 1 and centerFrames.len >= 3 and + centerFrames[2] == fpMaximize): 0 else: - 0) + (attr.width div 2) - (if i == 0 and self.config.frameParts.center.len > 1 and self.config.frameParts.center.find(fpTitle) != -1: self.config.buttonSize.cint + - extent.width div 2 else: 0), self.config.buttonOffset.y.cint) + 0 + ) + (attr.width div 2) - ( + if (i == 0 and centerFrames.len > 1 and + centerFrames.find(fpTitle) != -1): + self.config.buttonSize.cint + extent.width div 2 + else: + 0), + self.config.buttonOffset.y.cint + ) + discard self.dpy.XMapWindow client.frame.close - discard XPutImage(self.dpy, client.frame.close, gc, image, 0, 0, 0, 0, self.config.buttonSize.cuint, self.config.buttonSize.cuint) + + self.XPutImage(image, client.frame.close, gc) + of fpMaximize: + maximizeExists = true - if not fileExists self.config.maximizePath: continue + + if not fileExists self.config.maximizePath: + continue + discard self.dpy.XMapWindow client.frame.maximize + + let + btnSize = self.config.buttonSize.cint + btnXOffset = self.config.buttonOffset.x.cint + btnYOffset = self.config.buttonOffset.y.cint + textXOffset = self.config.textOffset.x.cint + centerFrames = self.config.frameParts.center + # M;T;C - discard self.dpy.XMoveWindow(client.frame.maximize, (if i == - 0: -self.config.buttonOffset.x.cint else: self.config.buttonOffset.x.cint) + - (if i == 1 and self.config.frameParts.center[0] == fpTitle: - self.config.textOffset.x.cint + extent.width div 2 - elif i == 1 and self.config.frameParts.center[0] == fpClose and self.config.frameParts.center.len>2: - -(extent.width div 2) - self.config.buttonOffset.x.cint - elif i == 1 and self.config.frameParts.center[0] == fpClose: - self.config.buttonSize.cint - elif i == 2 and self.config.frameParts.center[1] == fpTitle: + discard self.dpy.XMoveWindow( + client.frame.maximize, + (if i == 0: btnXOffset else: btnXOffset) + ( + if i == 1 and centerFrames[0] == fpTitle: + textXOffset + extent.width div 2 + elif i == 1 and centerFrames[0] == fpClose and centerFrames.len > 2: + -(extent.width div 2) - btnXOffset.cint + elif i == 1 and centerFrames[0] == fpClose: + btnSize + elif i == 2 and centerFrames[1] == fpTitle: extent.width div 2 - elif i == 2 and self.config.frameParts.center[1] == fpClose: - extent.width div 2 + self.config.buttonSize.cint + self.config.buttonOffset.x.cint - elif i == 0 and self.config.frameParts.center.len > 2: + elif i == 2 and centerFrames[1] == fpClose: + extent.width div 2 + btnSize + btnXOffset + elif i == 0 and centerFrames.len > 2: # meh - -(extent.width div 2) - self.config.buttonOffset.x.cint + -(extent.width div 2) - btnXOffset elif i == 0: - -self.config.buttonOffset.x.cint - else: 0) + (attr.width div 2), self.config.buttonOffset.y.cint) - var - screen = newImage(self.config.buttonSize.int, self.config.buttonSize.int) - let buttonColor = cast[array[3, uint8]](fp) - screen.fill(rgba(buttonColor[2],buttonColor[1],buttonColor[0],255)) - let img = readImage(self.config.maximizePath) - screen.draw( - img, - # translate(vec2(100, 100)) * - scale(vec2(self.config.buttonSize.int / img.width, self.config.buttonSize.int / img.height)) - # translate(vec2(-450, -450)) + -btnXOffset + else: + 0 + ) + (attr.width div 2), + btnYOffset ) - log $attr.depth - var ctx = newContext screen - # convert to BGRA - var frameBufferEndian = ctx.image.data - for i, color in frameBufferEndian: - let x = color - # RGBX -> BGRX - frameBufferEndian[i].r = x.b - frameBufferEndian[i].b = x.r - var frameBuffer = addr frameBufferEndian[0] - let image = XCreateImage(self.dpy, attr.visual, attr.depth.cuint, ZPixmap, 0, cast[cstring]( - frameBuffer), self.config.buttonSize.cuint, self.config.buttonSize.cuint, 8, cint(self.config.buttonSize*4)) - discard XPutImage(self.dpy, client.frame.maximize, gc, image, 0, 0, 0, 0, self.config.buttonSize.cuint, self.config.buttonSize.cuint) + + let image = self.XCreateImage(self.config.maximizePath, fp, attr) + + self.XPutImage(image, client.frame.maximize, gc) + for i, part in self.config.frameParts.right: + case part: of fpTitle: - if not closeExists: discard self.dpy.XUnmapWindow client.frame.close - client.draw.XftDrawStringUtf8(addr client.color, self.font, - (if self.config.frameParts.right.len == 1 or (self.config.frameParts.right.len == 2 and i == 1 and self.config.frameParts.right[0] in {fpClose, fpMaximize}): - cint(attr.width) - (cint (extent.width) + self.config.textOffset.x.cint) - elif self.config.frameParts.right.len == 2 and i == 0 and self.config.frameParts.right[1] in {fpClose, fpMaximize}: - cint(attr.width) - (cint (extent.width) + self.config.textOffset.x.cint + self.config.buttonOffset.x.cint + self.config.buttonSize.cint) - elif i == 1 and self.config.frameParts.right.len == 3 and self.config.frameParts.right[0] in {fpClose, fpMaximize}: - cint(attr.width) - (cint (extent.width) + self.config.buttonSize.cint + self.config.buttonOffset.x.cint) - elif i == 2: - cint(attr.width) - (cint (extent.width) + self.config.textOffset.x.cint) - elif i == 0 and self.config.frameParts.right.len == 3: - cint(attr.width) - (extent.width.cint + self.config.buttonSize.cint * 2 + self.config.buttonOffset.x.cint * 3) - else: 0), cint self.config.textOffset.y, - cast[ - ptr char](cstring client.title), cint client.title.len) + + if not closeExists: + discard self.dpy.XUnmapWindow client.frame.close + + let + rightFrames = self.config.frameParts.right + textXOffset = self.config.textOffset.x.cint + btnXOffset = self.config.buttonOffset.x.cint + btnSize = self.config.buttonSize.cint + + client.draw.XftDrawStringUtf8( + addr client.color, + self.font, + ( + if (rightFrames.len == 1 or (rightFrames.len == 2 and i == 1 and + rightFrames[0] in {fpClose, fpMaximize})): + attr.width.cint - (extent.width.cint + textXOffset) + elif (rightFrames.len == 2 and i == 0 and + rightFrames[1] in {fpClose, fpMaximize}): + attr.width.cint - extent.width.cint - textXOffset - btnXOffset - btnSize + elif (i == 1 and rightFrames.len == 3 and + rightFrames[0] in {fpClose, fpMaximize}): + attr.width.cint - (extent.width.cint + btnSize + btnXOffset) + elif i == 2: + attr.width.cint - extent.width.cint - textXOffset + elif i == 0 and rightFrames.len == 3: + attr.width.cint - extent.width.cint - btnSize * 2 - btnXOffset * 3 + else: 0 + ), + self.config.textOffset.y.cint, + cast[ptr char](cstring client.title), + client.title.len.cint + ) + of fpClose: + closeExists = true - if not fileExists self.config.closePath: continue - var - screen = newImage(self.config.buttonSize.int, self.config.buttonSize.int) - let buttonColor = cast[array[3, uint8]](fp) - screen.fill(rgba(buttonColor[2], buttonColor[1], buttonColor[0], 255)) - let img = readImage(self.config.closePath) - screen.draw( - img, - # translate(vec2(100, 100)) * - scale(vec2(self.config.buttonSize.int / img.width, self.config.buttonSize.int / img.height)) - # translate(vec2(-450, -450)) + + if not fileExists self.config.closePath: + continue + + let image = self.XCreateImage(self.config.closePath, fp, attr) + + let + btnXOffset = self.config.buttonOffset.x.cint + btnYOffset = self.config.buttonOffset.y.cint + btnSize = self.config.buttonSize.cint + rightFrames = self.config.frameParts.right + + discard self.dpy.XMoveWindow( + client.frame.close, + (if i == 0: - btnXOffset else: btnXOffset) + + ( + if i == 1 and rightFrames.len == 2: + - self.config.buttonOffset.x.cint*2 #-self.config.buttonSize.cint + elif i == 1 and rightFrames.len == 3: + - extent.width - btnXOffset + elif i == 0 and rightFrames.len == 2 and rightFrames[1] == fpTitle: + - extent.width + elif (i == 0 and rightFrames.len == 2 and rightFrames[1] == fpMaximize): + - btnSize - btnXOffset + elif i == 0 and rightFrames.len == 3: + - btnSize - btnXOffset - extent.width + elif i == 2: + - btnSize + else: + 0 + ) + attr.width - btnSize - ( + if i == 0 and self.config.frameParts.center.len > 1: + btnSize + extent.width div 2 + else: + 0 + ), + btnYOffset ) - log $attr.depth - var ctx = newContext screen - # convert to BGRA - var frameBufferEndian = ctx.image.data - for i, color in frameBufferEndian: - let x = color - # RGBX -> BGRX - frameBufferEndian[i].r = x.b - frameBufferEndian[i].b = x.r - var frameBuffer = addr frameBufferEndian[0] - let image = XCreateImage(self.dpy, attr.visual, attr.depth.cuint, ZPixmap, 0, cast[cstring]( - frameBuffer), self.config.buttonSize.cuint, self.config.buttonSize.cuint, 8, cint(self.config.buttonSize*4)) - discard self.dpy.XMoveWindow(client.frame.close, (if i == - 0: -self.config.buttonOffset.x.cint else: self.config.buttonOffset.x.cint) + - (if i == 1 and self.config.frameParts.right.len == 2: - -self.config.buttonOffset.x.cint*2 #-self.config.buttonSize.cint - elif i == 1 and self.config.frameParts.right.len == 3: - -extent.width - self.config.buttonOffset.x.cint - elif i == 0 and self.config.frameParts.right.len == 2 and self.config.frameParts.right[1] == fpTitle: - -extent.width - elif i == 0 and self.config.frameParts.right.len == 2 and self.config.frameParts.right[1] == fpMaximize: - -self.config.buttonSize.cint - self.config.buttonOffset.x.cint - elif i == 0 and self.config.frameParts.right.len == 3: - -self.config.buttonSize.cint - (self.config.buttonOffset.x.cint + extent.width) - elif i == 2: - -self.config.buttonSize.cint - else: 0) + (attr.width) - self.config.buttonSize.cint - (if i == 0 and - self.config.frameParts.center.len > 1: self.config.buttonSize.cint + - extent.width div 2 else: 0), self.config.buttonOffset.y.cint) + discard self.dpy.XMapWindow client.frame.close - discard XPutImage(self.dpy, client.frame.close, gc, image, 0, 0, 0, 0, self.config.buttonSize.cuint, self.config.buttonSize.cuint) + + self.XPutImage(image, client.frame.close, gc) + of fpMaximize: + maximizeExists = true - if not fileExists self.config.maximizePath: continue + + if not fileExists self.config.maximizePath: + continue + discard self.dpy.XMapWindow client.frame.maximize - discard self.dpy.XMoveWindow(client.frame.maximize, - self.config.buttonOffset.x.cint + ( - if i == 1 and self.config.frameParts.right[0] == fpTitle and self.config.frameParts.right.len == 3: - - self.config.buttonSize.cint * 2 - self.config.buttonOffset.x.cint - elif i == 1 and self.config.frameParts.right[0] == fpTitle: - - self.config.buttonSize.cint - elif i == 1 and self.config.frameParts.right[0] == fpClose and self.config.frameParts.right.len == 3 and self.config.frameParts.right[2] == fpTitle: - -extent.width - (self.config.buttonOffset.x.cint * 2) - elif i == 1 and self.config.frameParts.right[0] == fpClose: - - self.config.buttonOffset.x.cint * 2 - elif i == 2: - -(self.config.buttonOffset.x.cint * 2) - elif i == 0 and self.config.frameParts.right.len == 2 and self.config.frameParts.right[1] == fpClose: - -(self.config.buttonOffset.x.cint * 4) - self.config.buttonSize.cint - elif i == 0 and self.config.frameParts.right.len > 2 and self.config.frameParts.right[1] == fpClose: - -(self.config.buttonOffset.x.cint * 3) - (self.config.buttonSize.cint + extent.width) - elif i == 0 and self.config.frameParts.right.len >= 2 and self.config.frameParts.right[1] == fpTitle: - -extent.width - self.config.buttonOffset.x.cint - self.config.buttonSize.cint*2 - elif i == 0 and self.config.frameParts.right.len == 1: - - self.config.buttonOffset.x.cint * 2 - else: 0) + (attr.width) - self.config.buttonSize.cint, self.config.buttonOffset.y.cint) - var - screen = newImage(self.config.buttonSize.int, self.config.buttonSize.int) - let buttonColor = cast[array[3, uint8]](fp) - screen.fill(rgba(buttonColor[2],buttonColor[1],buttonColor[0],255)) - let img = readImage(self.config.maximizePath) - screen.draw( - img, - # translate(vec2(100, 100)) * - scale(vec2(self.config.buttonSize.int / img.width, self.config.buttonSize.int / img.height)) - # translate(vec2(-450, -450)) + + let + rightFrames = self.config.frameParts.right + btnSize = self.config.buttonSize.cint + btnXOffset = self.config.buttonOffset.x.cint + + offset = + if i == 1 and rightFrames[0] == fpTitle and rightFrames.len == 3: + - btnSize * 2 - btnXOffset + elif i == 1 and rightFrames[0] == fpTitle: + - btnSize + elif (i == 1 and rightFrames[0] == fpClose and + rightFrames.len == 3 and rightFrames[2] == fpTitle): + - extent.width - btnXOffset * 2 + elif i == 1 and rightFrames[0] == fpClose: + - btnXOffset * 2 + elif i == 2: + - btnXOffset * 2 + elif i == 0 and rightFrames.len == 2 and rightFrames[1] == fpClose: + - btnXOffset * 4 - btnSize + elif i == 0 and rightFrames.len > 2 and rightFrames[1] == fpClose: + - btnXOffset * 3 - btnSize - extent.width + elif i == 0 and rightFrames.len >= 2 and rightFrames[1] == fpTitle: + - extent.width - btnXOffset - btnSize*2 + elif i == 0 and rightFrames.len == 1: + - btnXOffset * 2 + else: + 0 + + discard self.dpy.XMoveWindow( + client.frame.maximize, + self.config.buttonOffset.x.cint + offset + attr.width - + self.config.buttonSize.cint, + self.config.buttonOffset.y.cint + ) + + let image = self.XCreateImage(self.config.maximizePath, fp, attr) + + self.XPutImage(image, client.frame.maximize, gc) + +proc tileWindows*(self: var Wm) = + + log "Tiling windows" + + var + clientLen: uint = 0 + master: ptr Client = nil + + let struts = self.config.struts + + for i, client in self.clients: + if client.fullscreen: + return # causes issues + + if client.tags == self.tags and not client.floating: + # We only care about clients on the current tag. + if master == nil: + # This must be the first client on the tag, otherwise master would not be + # nil; therefore, we promote it to master. + master = addr self.clients[i] + inc clientLen + + if master == nil: + return + + if clientLen == 0: + return # we got nothing to tile. + + var + scrNo: cint + scrInfo = cast[ptr UncheckedArray[XineramaScreenInfo]]( + self.dpy.XineramaQueryScreens(addr scrNo) + ) + + # echo cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) + let masterWidth = + if clientLen == 1: + uint scrInfo[0].width - struts.left.cint - + struts.right.cint - self.config.borderWidth.cint*2 + else: + uint scrInfo[0].width shr 1 - struts.left.cint - + self.config.borderWidth.cint*2 + + log $masterWidth + + let h = ( + scrInfo[0].height - struts.top.int16 - struts.bottom.int16 - + self.config.borderWidth.cint*2 + ).cuint + discard self.dpy.XMoveResizeWindow( + master.frame.window, + struts.left.cint, + struts.top.cint, + masterWidth.cuint, + h + ) + + discard self.dpy.XResizeWindow( + master.window, + masterWidth.cuint, + (scrInfo[0].height - struts.top.cint - struts.bottom.cint - + master.frameHeight.cint - self.config.borderWidth.cint*2).cuint + ) + + for win in [master.frame.title, master.frame.top]: + discard self.dpy.XResizeWindow( + win, + masterWidth.cuint, + master.frameHeight.cuint + ) + + self.renderTop master[] + + # discard self.dpy.XMoveResizeWindow(master.frame.window, cint self.config.struts.left, cint self.config.struts.top, cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth * 2) - self.config.gaps*2 - int16 self.config.struts.right, cuint scrInfo[0].height - int16(self.config.borderWidth * 2) - int16(self.config.struts.top) - int16(self.config.struts.bottom)) # bring the master window up to cover half the screen + # discard self.dpy.XResizeWindow(master.window, cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth*2) - self.config.gaps*2 - int16 self.config.struts.right, cuint scrInfo[0].height - int16(self.config.borderWidth*2) - int16(self.config.frameHeight) - int16(self.config.struts.top) - int16(self.config.struts.bottom)) # bring the master window up to cover half the screen + + var irrevelantLen: uint = 0 + for i, client in self.clients: + if client.tags != self.tags or client == master[] or client.floating: + inc irrevelantLen + continue + + if clientLen == 2: + discard self.dpy.XMoveWindow( + client.frame.window, + (scrInfo[0].width shr 1 + self.config.gaps).cint, + struts.top.cint + ) + + let w = + cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - + int16(self.config.borderWidth*2) - + self.config.gaps - self.config.struts.right.cint + + discard self.dpy.XResizeWindow( + client.frame.top, + w, + self.config.frameHeight.cuint + ) + + discard self.dpy.XResizeWindow( + client.frame.title, + w, + self.config.frameHeight.cuint + ) + + discard self.dpy.XResizeWindow( + client.window, + w, + (scrInfo[0].height - struts.top.cint - struts.bottom.cint - + client.frameHeight.cint - self.config.borderWidth.cint*2).cuint ) - log $attr.depth - var ctx = newContext screen - # convert to BGRA - var frameBufferEndian = ctx.image.data - for i, color in frameBufferEndian: - let x = color - # RGBX -> BGRX - frameBufferEndian[i].r = x.b - frameBufferEndian[i].b = x.r - var frameBuffer = addr frameBufferEndian[0] - let image = XCreateImage(self.dpy, attr.visual, attr.depth.cuint, ZPixmap, 0, cast[cstring]( - frameBuffer), self.config.buttonSize.cuint, self.config.buttonSize.cuint, 8, cint(self.config.buttonSize*4)) - discard XPutImage(self.dpy, client.frame.maximize, gc, image, 0, 0, 0, 0, self.config.buttonSize.cuint, self.config.buttonSize.cuint) - -proc maximizeClient*(self: var Wm; client: var Client, force: bool = false, forceun: bool = false): void = + + else: + # How many windows are there in the stack? We must subtract 1 to ignore + # the master window; which we iterate over too. + let stackElem = i - int irrevelantLen - 1 + + let yGap = + if stackElem != 0: + self.config.gaps + else: + 0 + + # let subStrut = if stackElem = clientLen + # XXX: the if stackElem == 1: 0 else: self.config.gaps is a huge hack + # and also incorrect behavior; while usually un-noticeable it makes the + # top window in the stack bigger by the gaps. Fix this!! + let w = ( + scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - + self.config.borderWidth.int16 * 2 - self.config.gaps - struts.right.cint + ).cuint + + discard self.dpy.XResizeWindow( + client.frame.top, + w, + self.config.frameHeight.cuint + ) + + discard self.dpy.XResizeWindow( + client.frame.title, + w, + self.config.frameHeight.cuint + ).cint + + var h = ( + ((scrInfo[0].height.float - struts.bottom.float - struts.top.float) * + ((i - irrevelantLen.int) / clientLen.int - 1)).cint + struts.top.cint + + (if stackElem == 1: 0 else: self.config.gaps.cint) + ) + + discard self.dpy.XMoveWindow( + client.frame.window, + (scrInfo[0].width shr 1 + yGap).cint, + h + ) + + var h2 = ( + ((scrInfo[0].height - struts.bottom.cint - struts.top.cint) div + (clientLen - 1).int16) - self.config.borderWidth.int16*2 - + client.frameHeight.int16 - (if stackElem == 1: 0 else: self.config.gaps) + ).cuint + + discard self.dpy.XResizeWindow( + client.window, + w, + h2 + ) + # the number of windows on the stack is i (the current client) minus the + # master window minus any irrevelant windows + # discard self.dpy.XMoveResizeWindow(client.frame.window, cint scrInfo[0].width shr 1, cint(float(scrInfo[0].height) * ((i - int irrevelantLen) / int clientLen - 1)) + cint self.config.gaps, cuint scrInfo[0].width shr 1 - int16(self.config.borderWidth * 2) - self.config.gaps, cuint (scrInfo[0].height div int16(clientLen - 1)) - int16(self.config.struts.bottom) - int16(self.config.borderWidth * 2) - self.config.gaps) # bring the master window up to cover half the screen + # discard self.dpy.XResizeWindow(client.window, cuint scrInfo[0].width shr (if clientLen == 1: 0 else: 1) - int16(self.config.borderWidth*2) - self.config.gaps, cuint (scrInfo[0].height div int16(clientLen - 1)) - int16(self.config.struts.bottom) - int16(self.config.borderWidth*2) - int16(self.config.frameHeight) - self.config.gaps) # bring the master window up to cover half the screen + + self.renderTop self.clients[i] + + discard self.dpy.XSync false + + discard self.dpy.XFlush + +proc maximizeClient*( + self: var Wm; + client: var Client, + force = false, + forceun = false + ) = + if (not force and client.maximized) or (force and forceun): - if client.beforeGeomMax.isNone: return + if client.beforeGeomMax.isNone: + return + + let geom = get client.beforeGeomMax + client.maximized = false - discard self.dpy.XMoveResizeWindow(client.frame.window, - cint client.beforeGeomMax.get.x, cint client.beforeGeomMax.get.y, - cuint client.beforeGeomMax.get.width, - cuint client.beforeGeomMax.get.height) - discard self.dpy.XMoveResizeWindow(client.window, - 0, cint self.config.frameHeight, - cuint client.beforeGeomMax.get.width, - cuint client.beforeGeomMax.get.height - self.config.frameHeight) - discard self.dpy.XChangeProperty(client.window, self.netAtoms[ - NetWMState], XaAtom, 32, PropModeReplace, cast[cstring]([]), 0) + + discard self.dpy.XMoveResizeWindow( + client.frame.window, + geom.x.cint, + geom.y.cint, + geom.width.cuint, + geom.height.cuint + ) + + discard self.dpy.XMoveResizeWindow( + client.window, + 0, + cint self.config.frameHeight.cint, + cuint geom.width.cuint, + (geom.height - self.config.frameHeight).cuint + ) + + discard self.dpy.XChangeProperty( + client.window, + self.netAtoms[NetWMState], + XaAtom, + 32, + PropModeReplace, + cast[cstring]([]), + 0 + ) + self.renderTop client return + client.maximized = true + # maximize the provided client - var scrNo: cint - var scrInfo = cast[ptr UncheckedArray[XineramaScreenInfo]]( - self.dpy.XineramaQueryScreens(addr scrNo)) - if scrInfo == nil: return + var + scrNo: cint + scrInfo = cast[ptr UncheckedArray[XineramaScreenInfo]]( + self.dpy.XineramaQueryScreens(addr scrNo) + ) + + if scrInfo == nil: + return + # where the hell is our window at var attr: XWindowAttributes discard self.dpy.XGetWindowAttributes(client.frame.window, addr attr) - client.beforeGeomMax = some Geometry(x: attr.x, y: attr.y, width: uint attr.width, height: uint attr.height) - var x: int - var y: int - var width: uint - var height: uint - if scrno == 1: + + client.beforeGeomMax = some Geometry( + x: attr.x, + y: attr.y, + width: attr.width.uint, + height: attr.height.uint + ) + + var + x: int + y: int + width: uint + height: uint + + if scrNo == 1: # 1st monitor, cuz only one x = 0 y = 0 width = scrInfo[0].width.uint height = scrInfo[0].height.uint else: - var cumulWidth = 0 - var cumulHeight = 0 + var + cumulWidth = 0 + cumulHeight = 0 + for i in countup(0, scrNo - 1): cumulWidth += scrInfo[i].width cumulHeight += scrInfo[i].height + if attr.x <= cumulWidth - attr.width: x = scrInfo[i].xOrg y = scrInfo[i].yOrg width = scrInfo[i].width.uint height = scrInfo[i].height.uint - let masterWidth = - uint scrInfo[0].width - - self.config.struts.left.cint - self.config.struts.right.cint - (self.config.borderWidth.cint*2) - discard self.dpy.XMoveWindow(client.frame.window, - cint self.config.struts.left + uint x, cint self.config.struts.top + uint y) - discard self.dpy.XResizeWindow(client.frame.window, cuint masterWidth, - cuint(height - self.config.struts.top - - self.config.struts.bottom - self.config.borderWidth.cuint*2)) - discard self.dpy.XResizeWindow(client.window, cuint masterWidth, - cuint(height - self.config.struts.top - - self.config.struts.bottom - client.frameHeight - self.config.borderWidth.cuint*2)) - for win in [client.frame.top, client.frame.title]: discard self.dpy.XResizeWindow(win, cuint masterWidth, cuint self.config.frameHeight) + + let strut = self.config.struts + + discard self.dpy.XMoveWindow( + client.frame.window, + (strut.left + x.uint).cint, + (strut.top + y.uint).cint + ) + + let masterWidth = ( + scrInfo[0].width - strut.left.cint - strut.right.cint - + self.config.borderWidth.cint*2 + ).uint + discard self.dpy.XResizeWindow( + client.frame.window, + masterWidth.cuint, + (height - strut.top - strut.bottom - self.config.borderWidth.cuint*2).cuint + ) + + discard self.dpy.XResizeWindow( + client.window, + cuint masterWidth, + cuint( + height - strut.top - strut.bottom - + client.frameHeight - self.config.borderWidth.cuint*2 + ) + ) + + for win in [client.frame.top, client.frame.title]: + discard self.dpy.XResizeWindow( + win, + masterWidth.cuint, + self.config.frameHeight.cuint + ) + var states = [NetWMStateMaximizedHorz, NetWMStateMaximizedVert] - discard self.dpy.XChangeProperty(client.window, self.netAtoms[ - NetWMState], XaAtom, 32, PropModeReplace, cast[cstring](addr states), 0) + + discard self.dpy.XChangeProperty( + client.window, + self.netAtoms[NetWMState], + XaAtom, + 32, + PropModeReplace, + cast[cstring](addr states), + 0 + ) + discard self.dpy.XSync false + discard self.dpy.XFlush + self.renderTop client -proc updateClientList(self: Wm): void = - let wins = self.clients.map do (client: Client) -> - Window: client.window # Retrieve all the underlying X11 windows from the client list. - if wins.len == 0: return +proc updateClientList*(self: Wm) = + let wins = self.clients.mapIt(it.window) + + if wins.len == 0: + return + discard self.dpy.XChangeProperty( self.root, self.netAtoms[NetClientList], @@ -630,10 +958,10 @@ proc updateClientList(self: Wm): void = 32, PropModeReplace, cast[cstring](unsafeAddr wins[0]), - cint wins.len + wins.len.cint ) -proc updateTagState*(self: Wm): void = +proc updateTagState*(self: Wm) = for client in self.clients: for i, tag in client.tags: if self.tags[i] and tag: @@ -641,24 +969,3 @@ proc updateTagState*(self: Wm): void = break discard self.dpy.XUnmapWindow client.frame.window -proc eventLoop*(self: var Wm): void = - if fileExists expandTilde "~/.config/worm/rc": - discard startProcess expandTilde "~/.config/worm/rc" - while true: - discard self.dpy.XNextEvent(unsafeAddr self.currEv) - self.dispatchEvent self.currEv - -proc dispatchEvent*(self: var Wm; ev: XEvent): void = - case ev.theType: - of ButtonPress: self.handleButtonPress ev.xbutton - of ButtonRelease: self.handleButtonRelease ev.xbutton - of MotionNotify: self.handleMotionNotify ev.xmotion - of MapRequest: self.handleMapRequest ev.xmaprequest - of ConfigureRequest: self.handleConfigureRequest ev.xconfigurerequest - of ConfigureNotify: self.handleConfigureNotify ev.xconfigure - of UnmapNotify: self.handleUnmapNotify ev.xunmap - of DestroyNotify: self.handleDestroyNotify ev.xdestroywindow - of ClientMessage: self.handleClientMessage ev.xclient - of Expose: self.handleExpose ev.xexpose - of PropertyNotify: self.handlePropertyNotify ev.xproperty - else: discard diff --git a/src/wormc.nim b/src/wormc.nim index 5300d72..54f44f4 100644 --- a/src/wormc.nim +++ b/src/wormc.nim @@ -1,7 +1,7 @@ -import x11/[x, xlib, xutil] -import std/[strutils, os] -# import log -import atoms +import + std/[strutils, os], + x11/[x, xlib, xutil], + atoms converter toXBool(x: bool): XBool = x.XBool converter toBool(x: XBool): bool = x.bool @@ -9,136 +9,157 @@ converter toBool(x: XBool): bool = x.bool type Layout = enum lyFloating, lyTiling -proc main: void = +proc formatMess(a: Atom, params: varargs[string, `$`]): array[5, clong] = + result[0] = a.clong + + for i in 1 ..< 5: + if i < params.len: + result[i] = params[i].parseInt().clong + +proc sendStrPrep(dpy: PDisplay, a: Atom, param: string) = + var + fontList = param.cstring + fontProp: XTextProperty + discard dpy.XUtf8TextListToTextProperty( + addr fontList, + 1, + XUTF8StringStyle, + addr fontProp + ) + dpy.XSetTextProperty( + dpy.XDefaultRootWindow, + addr fontProp, + a + ) + discard XFree fontProp.value + +proc getLayoutOrd(s: string): int = + if s == "floating": + result = lyFloating.ord.clong + elif s == "tiling": + result = lyTiling.ord.clong + else: + quit 1 + +proc main = let dpy = XOpenDisplay nil if dpy == nil: return - let ipcAtoms = dpy.getIpcAtoms - let root = dpy.XDefaultRootWindow - let params = commandLineParams() + + let + ipcAtoms = dpy.getIpcAtoms + root = dpy.XDefaultRootWindow + params = commandLineParams() for i, param in commandLineParams(): var data: array[5, clong] case param: - of "border-active-pixel": data = [clong ipcAtoms[IpcBorderActivePixel], - clong params[i+1].parseInt, 0, 0, 0] - of "border-inactive-pixel": data = [clong ipcAtoms[IpcBorderInactivePixel], - clong params[i+1].parseInt, 0, 0, 0] - of "border-width": data = [clong ipcAtoms[IpcBorderWidth], - clong params[i+1].parseInt, 0, 0, 0] - of "frame-active-pixel": data = [clong ipcAtoms[IpcFrameActivePixel], - clong params[i+1].parseInt, 0, 0, 0] - of "frame-inactive-pixel": data = [clong ipcAtoms[IpcFrameInactivePixel], - clong params[i+1].parseInt, 0, 0, 0] - of "frame-height": data = [clong ipcAtoms[IpcFrameHeight], - clong params[i+1].parseInt, 0, 0, 0] - of "text-active-pixel": data = [clong ipcAtoms[IpcTextActivePixel], - clong params[i+1].parseInt, 0, 0, 0] - of "text-inactive-pixel": data = [clong ipcAtoms[IpcTextInactivePixel], - clong params[i+1].parseInt, 0, 0, 0] - of "gaps": data = [clong ipcAtoms[IpcGaps], - clong params[i+1].parseInt, 0, 0, 0] - of "text-font": # This is not as simple as sending a ClientMessage to the root window, because a string is involved; therefore, we must first do a bit of prepreation and then send a data-less msg - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcTextFont]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcTextFont], 0, 0, 0, 0] - of "frame-left": # Ditto - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcFrameLeft]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcFrameLeft], 0, 0, 0, 0] - of "frame-center": # Ditto - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcFrameCenter]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcFrameCenter], 0, 0, 0, 0] - of "frame-right": # Ditto - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcFrameRight]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcFrameRight], 0, 0, 0, 0] - of "root-menu": # Ditto - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcRootMenu]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcRootMenu], 0, 0, 0, 0] - of "close-path": # Ditto - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcClosePath]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcClosePath], 0, 0, 0, 0] - of "maximize-path": # Ditto - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcMaximizePath]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcMaximizePath], 0, 0, 0, 0] - of "decoration-disable": # Ditto - var fontList = cstring params[i+1] - var fontProp: XTextProperty - discard dpy.XUtf8TextListToTextProperty(addr fontList, 1, - XUTF8StringStyle, addr fontProp) - dpy.XSetTextProperty(root, addr fontProp, ipcAtoms[IpcDecorationDisable]) - discard XFree fontProp.value - data = [clong ipcAtoms[IpcDecorationDisable], 0, 0, 0, 0] - of "text-offset": data = [clong ipcAtoms[IpcTextOffset], - clong params[i+1].parseInt, clong params[i+2].parseInt, 0, 0] - of "kill-client": data = [clong ipcAtoms[IpcKillClient], clong params[ - i+1].parseInt, 0, 0, 0] - of "kill-active-client": data = [clong ipcAtoms[IpcKillClient], 0, 0, 0, 0] - of "close-client": data = [clong ipcAtoms[IpcCloseClient], clong params[ - i+1].parseInt, 0, 0, 0] - of "close-active-client": data = [clong ipcAtoms[IpcCloseClient], 0, 0, 0, 0] - of "switch-tag": data = [clong ipcAtoms[IpcSwitchTag], clong params[ - i+1].parseInt, 0, 0, 0] - of "layout": data = [clong ipcAtoms[IpcLayout], if params[i+1] == - "floating": clong lyFloating elif params[i+1] == - "tiling": clong lyTiling else: quit(1), 0, 0, 0] - of "struts": data = [clong ipcAtoms[IpcStruts], clong params[i+1].parseInt, - clong params[i+2].parseInt, clong params[i+3].parseInt, clong params[i+4].parseInt] - of "move-tag": data = [clong ipcAtoms[IpcMoveTag], clong params[ - i+1].parseInt, clong params[i+2].parseInt, 0, 0] - of "move-active-tag": data = [clong ipcAtoms[IpcMoveTag], clong params[ - i+1].parseInt, 0, 0, 0] - of "master": data = [clong ipcAtoms[IpcMaster], clong params[i+1].parseInt, - 0, 0, 0] - of "master-active": data = [clong ipcAtoms[IpcMaster], 0, 0, 0, 0] - of "float": data = [clong ipcAtoms[IpcFloat], clong params[i+1].parseInt, 0, 0, 0] - of "float-active": data = [clong ipcAtoms[IpcFloat], 0, 0, 0, 0] - of "button-offset": data = [clong ipcAtoms[IpcButtonOffset], - clong params[i+1].parseInt, clong params[i+2].parseInt, 0, 0] - of "button-size": data = [clong ipcAtoms[IpcButtonSize], clong params[i+1].parseInt, 0, 0, 0] - of "maximize-client": data = [clong ipcAtoms[IpcMaximizeClient], clong params[i+1].parseInt, 0, 0, 0] - of "maximize-active-client": data = [clong ipcAtoms[IpcMaximizeClient], 0, 0, 0, 0] + of "border-active-pixel": + data = ipcAtoms[IpcBorderActivePixel].formatMess(params[i+1]) + of "border-inactive-pixel": + data = ipcAtoms[IpcBorderInactivePixel].formatMess(params[i+1]) + of "border-width": + data = ipcAtoms[IpcBorderWidth].formatMess(params[i+1]) + of "frame-active-pixel": + data = ipcAtoms[IpcFrameActivePixel].formatMess(params[i+1]) + of "frame-inactive-pixel": + data = ipcAtoms[IpcFrameInactivePixel].formatMess(params[i+1]) + of "frame-height": + data = ipcAtoms[IpcFrameHeight].formatMess(params[i+1]) + of "text-active-pixel": + data = ipcAtoms[IpcTextActivePixel].formatMess(params[i+1]) + of "text-inactive-pixel": + data = ipcAtoms[IpcTextInactivePixel].formatMess(params[i+1]) + of "gaps": + data = ipcAtoms[IpcGaps].formatMess(params[i+1]) + + # This is not as simple as sending a ClientMessage to the root window, + # because a string is involved; therefore, we must first do a bit of + # preparation and then send a data-less msg. + of "text-font": + dpy.sendStrPrep(ipcAtoms[IpcTextFont], params[i+1]) + data = ipcAtoms[IpcTextFont].formatMess() + of "frame-left": + dpy.sendStrPrep(ipcAtoms[IpcFrameLeft], params[i+1]) + data = ipcAtoms[IpcFrameLeft].formatMess() + of "frame-center": + dpy.sendStrPrep(ipcAtoms[IpcFrameCenter], params[i+1]) + data = ipcAtoms[IpcFrameCenter].formatMess() + of "frame-right": + dpy.sendStrPrep(ipcAtoms[IpcFrameRight], params[i+1]) + data = ipcAtoms[IpcFrameRight].formatMess() + of "root-menu": + dpy.sendStrPrep(ipcAtoms[IpcRootMenu], params[i+1]) + data = formatMess ipcAtoms[IpcRootMenu] + of "close-path": + dpy.sendStrPrep(ipcAtoms[IpcClosePath], params[i+1]) + data = ipcAtoms[IpcClosePath].formatMess() + of "maximize-path": + dpy.sendStrPrep(ipcAtoms[IpcMaximizePath], params[i+1]) + data = ipcAtoms[IpcMaximizePath].formatMess() + of "decoration-disable": + dpy.sendStrPrep(ipcAtoms[IpcDecorationDisable], params[i+1]) + data = ipcAtoms[IpcDecorationDisable].formatMess() + of "text-offset": + data = ipcAtoms[IpcTextOffset].formatMess(params[i+1], params[i+2]) + of "kill-client": + data = ipcAtoms[IpcKillClient].formatMess(params[i+1]) + of "kill-active-client": + data = ipcAtoms[IpcKillClient].formatMess() + of "close-client": + data = ipcAtoms[IpcCloseClient].formatMess(params[i+1]) + of "close-active-client": + data = ipcAtoms[IpcCloseClient].formatMess() + of "switch-tag": + data = ipcAtoms[IpcSwitchTag].formatMess(params[i+1]) + of "layout": + data = ipcAtoms[IpcLayout].formatMess(getLayoutOrd params[i+1]) + of "struts": + data = ipcAtoms[IpcStruts].formatMess( + params[i+1], params[i+2], params[i+3], params[i+4] + ) + of "move-tag": + data = ipcAtoms[IpcMoveTag].formatMess(params[i+1], params[i+2]) + of "move-active-tag": + data = ipcAtoms[IpcMoveTag].formatMess(params[i+1]) + of "master": + data = ipcAtoms[IpcMaster].formatMess(params[i+1]) + of "master-active": + data = ipcAtoms[IpcMaster].formatMess() + of "float": + data = ipcAtoms[IpcFloat].formatMess(params[i+1]) + of "float-active": + data = ipcAtoms[IpcFloat].formatMess() + of "button-offset": + data = ipcAtoms[IpcButtonOffset].formatMess(params[i+1], params[i+2]) + of "button-size": + data = ipcAtoms[IpcButtonSize].formatMess(params[i+1]) + of "maximize-client": + data = ipcAtoms[IpcMaximizeClient].formatMess(params[i+1]) + of "maximize-active-client": + data = ipcAtoms[IpcMaximizeClient].formatMess() else: discard - let event = XEvent(xclient: XClientMessageEvent(format: 32, - theType: ClientMessage, serial: 0, sendEvent: true, display: dpy, - window: root, messageType: ipcAtoms[IpcClientMessage], - data: XClientMessageData(l: data))) - discard dpy.XSendEvent(root, false, SubstructureNotifyMask, cast[ptr XEvent]( - unsafeAddr event)) + + let event = XEvent( + xclient: XClientMessageEvent( + format: 32, + theType: ClientMessage, + serial: 0, + sendEvent: true, + display: dpy, + window: root, + messageType: ipcAtoms[IpcClientMessage], + data: XClientMessageData(l: data) + ) + ) + discard dpy.XSendEvent( + root, + false, + SubstructureNotifyMask, + cast[ptr XEvent](unsafeAddr event) + ) discard dpy.XFlush discard dpy.XSync false -when isMainModule: main() +when isMainModule: + main()