From a041d9898e6d699bd8c0c25482ec574feb03c547 Mon Sep 17 00:00:00 2001 From: "John Ankarstr\\xf6m" Date: Sat, 29 May 2021 12:54:47 +0200 Subject: First commit This is the original state of the released tarball for JWM 1.8, which will serve as my starting point for further modifications. --- src/dock.c | 602 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 602 insertions(+) create mode 100644 src/dock.c (limited to 'src/dock.c') diff --git a/src/dock.c b/src/dock.c new file mode 100644 index 0000000..289e85f --- /dev/null +++ b/src/dock.c @@ -0,0 +1,602 @@ +/** + * @file dock.c + * @author Joe Wingbermuehle + * @date 2006 + * + * @brief Dock functions. + * + */ + +#include "jwm.h" +#include "dock.h" +#include "tray.h" +#include "main.h" +#include "error.h" +#include "color.h" + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +#define SYSTEM_TRAY_ORIENTATION_HORZ 0 +#define SYSTEM_TRAY_ORIENTATION_VERT 1 + +/** Structure to represent a docked window. */ +typedef struct DockNode { + + Window window; + int needs_reparent; + + struct DockNode *next; + +} DockNode; + +/** Structure to represent a dock tray component. */ +typedef struct DockType { + + TrayComponentType *cp; + + Window window; + + DockNode *nodes; + +} DockType; + +static const char *BASE_SELECTION_NAME = "_NET_SYSTEM_TRAY_S%d"; +static const char *ORIENTATION_ATOM = "_NET_SYSTEM_TRAY_ORIENTATION"; + +static DockType *dock = NULL; +static int owner = 0; +static Atom dockAtom; +static unsigned long orientation; + +static void SetSize(TrayComponentType *cp, int width, int height); +static void Create(TrayComponentType *cp); +static void Resize(TrayComponentType *cp); + +static void DockWindow(Window win); +static int UndockWindow(Window win); + +static void UpdateDock(); + +/** Initialize dock data. */ +void InitializeDock() { +} + +/** Startup the dock. */ +void StartupDock() { + + char *selectionName; + + if(!dock) { + /* No dock has been requested. */ + return; + } + + if(!dock->cp) { + /* The Dock item has been removed from the configuration. */ + JXDestroyWindow(display, dock->window); + Release(dock); + dock = NULL; + return; + } + + if(dock->window == None) { + + /* No dock yet. */ + + /* Get the selection atom. */ + selectionName = AllocateStack(strlen(BASE_SELECTION_NAME) + 1); + sprintf(selectionName, BASE_SELECTION_NAME, rootScreen); + dockAtom = JXInternAtom(display, selectionName, False); + ReleaseStack(selectionName); + + /* The location and size of the window doesn't matter here. */ + dock->window = JXCreateSimpleWindow(display, rootWindow, + /* x, y, width, height */ 0, 0, 1, 1, + /* border_size, border_color */ 0, 0, + /* background */ colors[COLOR_TRAY_BG]); + JXSelectInput(display, dock->window, + SubstructureNotifyMask + | SubstructureRedirectMask + | PointerMotionMask | PointerMotionHintMask); + + } + dock->cp->window = dock->window; + +} + +/** Shutdown the dock. */ +void ShutdownDock() { + + DockNode *np; + + if(dock) { + + if(shouldRestart) { + + /* If restarting we just reparent the dock window to the root + * window. We need to keep the dock around and visible so that + * we don't cause problems with the docked windows. + * It seems every application handles docking differently... + */ + JXReparentWindow(display, dock->window, rootWindow, 0, 0); + + } else { + + /* JWM is exiting. */ + + /* Release memory used by the dock list. */ + while(dock->nodes) { + np = dock->nodes->next; + JXReparentWindow(display, dock->nodes->window, rootWindow, 0, 0); + Release(dock->nodes); + dock->nodes = np; + } + + /* Release the selection. */ + if(owner) { + JXSetSelectionOwner(display, dockAtom, None, CurrentTime); + } + + /* Destroy the dock window. */ + JXDestroyWindow(display, dock->window); + + } + + } + +} + +/** Destroy dock data. */ +void DestroyDock() { + + if(dock) { + if(shouldRestart) { + dock->cp = NULL; + } else { + Release(dock); + dock = NULL; + } + } + +} + +/** Create a dock component. */ +TrayComponentType *CreateDock() { + + TrayComponentType *cp; + + if(dock != NULL && dock->cp != NULL) { + Warning("only one Dock allowed"); + return NULL; + } else if(dock == NULL) { + dock = Allocate(sizeof(DockType)); + dock->nodes = NULL; + dock->window = None; + } + + cp = CreateTrayComponent(); + cp->object = dock; + dock->cp = cp; + cp->requestedWidth = 1; + cp->requestedHeight = 1; + + cp->SetSize = SetSize; + cp->Create = Create; + cp->Resize = Resize; + + return cp; + +} + +/** Set the size of a dock component. */ +void SetSize(TrayComponentType *cp, int width, int height) { + + int count; + DockNode *np; + + Assert(cp); + Assert(dock); + + count = 0; + for(np = dock->nodes; np; np = np->next) { + ++count; + } + + if(width == 0) { + if(count > 0) { + cp->width = count * height; + cp->requestedWidth = cp->width; + } else { + cp->width = 1; + cp->requestedWidth = 1; + } + } else if(height == 0) { + if(count > 0) { + cp->height = count * width; + cp->requestedHeight = cp->height; + } else { + cp->height = 1; + cp->requestedHeight = 1; + } + } + +} + +/** Initialize a dock component. */ +void Create(TrayComponentType *cp) { + + XEvent event; + Atom orientationAtom; + + Assert(cp); + + /* Map the dock window. */ + if(cp->window != None) { + JXResizeWindow(display, cp->window, cp->width, cp->height); + JXMapRaised(display, cp->window); + } + + /* Set the orientation. */ + orientationAtom = JXInternAtom(display, ORIENTATION_ATOM, False); + if(cp->height == 1) { + orientation = SYSTEM_TRAY_ORIENTATION_VERT; + } else { + orientation = SYSTEM_TRAY_ORIENTATION_HORZ; + } + JXChangeProperty(display, dock->cp->window, orientationAtom, + XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&orientation, 1); + + /* Get the selection if we don't already own it. + * If we did already own it, getting it again would cause problems + * with some clients due to the way restarts are handled. + */ + if(!owner) { + + owner = 1; + JXSetSelectionOwner(display, dockAtom, dock->cp->window, CurrentTime); + if(JXGetSelectionOwner(display, dockAtom) != dock->cp->window) { + + owner = 0; + Warning("could not acquire system tray selection"); + + } else { + + memset(&event, 0, sizeof(event)); + event.xclient.type = ClientMessage; + event.xclient.window = rootWindow; + event.xclient.message_type = JXInternAtom(display, "MANAGER", False); + event.xclient.format = 32; + event.xclient.data.l[0] = CurrentTime; + event.xclient.data.l[1] = dockAtom; + event.xclient.data.l[2] = dock->cp->window; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + JXSendEvent(display, rootWindow, False, StructureNotifyMask, &event); + + } + + } + +} + +/** Resize a dock component. */ +void Resize(TrayComponentType *cp) { + + Assert(cp); + + JXResizeWindow(display, cp->window, cp->width, cp->height); + UpdateDock(); + +} + +/** Handle a dock event. */ +void HandleDockEvent(const XClientMessageEvent *event) { + + Assert(event); + + switch(event->data.l[1]) { + case SYSTEM_TRAY_REQUEST_DOCK: + DockWindow(event->data.l[2]); + break; + case SYSTEM_TRAY_BEGIN_MESSAGE: + break; + case SYSTEM_TRAY_CANCEL_MESSAGE: + break; + default: + Debug("invalid opcode in dock event"); + break; + } + +} + +/** Handle a resize request event. */ +int HandleDockResizeRequest(const XResizeRequestEvent *event) { + + DockNode *np; + + Assert(event); + + if(!dock) { + return 0; + } + + for(np = dock->nodes; np; np = np->next) { + if(np->window == event->window) { + + JXResizeWindow(display, np->window, event->width, event->height); + UpdateDock(); + + return 1; + } + } + + return 0; +} + +/** Handle a configure request event. */ +int HandleDockConfigureRequest(const XConfigureRequestEvent *event) { + + XWindowChanges wc; + DockNode *np; + + Assert(event); + + if(!dock) { + return 0; + } + + for(np = dock->nodes; np; np = np->next) { + if(np->window == event->window) { + wc.stack_mode = event->detail; + wc.sibling = event->above; + wc.border_width = event->border_width; + wc.x = event->x; + wc.y = event->y; + wc.width = event->width; + wc.height = event->height; + JXConfigureWindow(display, np->window, event->value_mask, &wc); + UpdateDock(); + return 1; + } + } + + return 0; + +} + +/** Handle a reparent notify event. */ +int HandleDockReparentNotify(const XReparentEvent *event) { + + DockNode *np; + int handled; + + Assert(event); + + /* Just return if there is no dock. */ + if(!dock) { + return 0; + } + + /* Check each docked window. */ + handled = 0; + for(np = dock->nodes; np; np = np->next) { + if(np->window == event->window) { + if(event->parent != dock->cp->window) { + /* For some reason the application reparented the window. + * We make note of this condition and reparent every time + * the dock is updated. Unfortunately we can't do this for + * all applications because some won't deal with it. + */ + np->needs_reparent = 1; + handled = 1; + } + } + } + + /* Layout the stuff on the dock again if something happened. */ + if(handled) { + UpdateDock(); + } + + return handled; + +} + +/** Handle a destroy event. */ +int HandleDockDestroy(Window win) { + + if(dock) { + return UndockWindow(win); + } else { + return 0; + } + +} + +/** Handle a selection clear event. */ +int HandleDockSelectionClear(const XSelectionClearEvent *event) { + + if(event->selection == dockAtom) { + Debug("lost _NET_SYSTEM_TRAY selection"); + owner = 0; + } + + return 0; + +} + +/** Add a window to the dock. */ +void DockWindow(Window win) { + + DockNode *np; + + Assert(dock); + + /* Make sure we have a valid window to add. */ + if(win == None) { + return; + } + + /* If this window is already docked ignore it. */ + for(np = dock->nodes; np; np = np->next) { + if(np->window == win) { + return; + } + } + + /* Add the window to our list. */ + np = Allocate(sizeof(DockNode)); + np->window = win; + np->needs_reparent = 0; + np->next = dock->nodes; + dock->nodes = np; + + /* Update the requested size. */ + if(orientation == SYSTEM_TRAY_ORIENTATION_HORZ) { + if(dock->cp->requestedWidth > 1) { + dock->cp->requestedWidth += dock->cp->height; + } else { + dock->cp->requestedWidth = dock->cp->height; + } + } else { + if(dock->cp->requestedHeight > 1) { + dock->cp->requestedHeight += dock->cp->width; + } else { + dock->cp->requestedHeight = dock->cp->width; + } + } + + /* It's safe to reparent at (0, 0) since we call + * ResizeTray which will invoke the Resize callback. + */ + JXAddToSaveSet(display, win); + JXSelectInput(display, win, + StructureNotifyMask + | ResizeRedirectMask + | PointerMotionMask | PointerMotionHintMask); + JXReparentWindow(display, win, dock->cp->window, 0, 0); + JXMapRaised(display, win); + + /* Resize the tray containing the dock. */ + ResizeTray(dock->cp->tray); + +} + +/** Remove a window from the dock. */ +int UndockWindow(Window win) { + + DockNode *np; + DockNode *last; + + Assert(dock); + + last = NULL; + for(np = dock->nodes; np; np = np->next) { + if(np->window == win) { + + /* Remove the window from our list. */ + if(last) { + last->next = np->next; + } else { + dock->nodes = np->next; + } + Release(np); + + /* Update the requested size. */ + if(orientation == SYSTEM_TRAY_ORIENTATION_HORZ) { + dock->cp->requestedWidth -= dock->cp->height; + if(dock->cp->requestedWidth <= 0) { + dock->cp->requestedWidth = 1; + } + } else { + dock->cp->requestedHeight -= dock->cp->width; + if(dock->cp->requestedHeight <= 0) { + dock->cp->requestedHeight = 1; + } + } + + /* Resize the tray. */ + ResizeTray(dock->cp->tray); + + return 1; + + } + last = np; + } + + return 0; +} + +/** Layout items on the dock. */ +void UpdateDock() { + + XWindowAttributes attr; + DockNode *np; + int x, y; + int width, height; + int xoffset, yoffset; + int itemWidth, itemHeight; + double ratio; + + Assert(dock); + + /* Determine the size of items in the dock. */ + if(orientation == SYSTEM_TRAY_ORIENTATION_HORZ) { + itemWidth = dock->cp->height; + itemHeight = dock->cp->height; + } else { + itemHeight = dock->cp->width; + itemWidth = dock->cp->width; + } + + x = 0; + y = 0; + for(np = dock->nodes; np; np = np->next) { + + xoffset = 0; + yoffset = 0; + width = itemWidth; + height = itemHeight; + + if(JXGetWindowAttributes(display, np->window, &attr)) { + + ratio = (double)attr.width / attr.height; + + if(ratio > 1.0) { + if(width > attr.width) { + width = attr.width; + } + height = width / ratio; + } else { + if(height > attr.height) { + height = attr.height; + } + width = height * ratio; + } + + xoffset = (itemWidth - width) / 2; + yoffset = (itemHeight - height) / 2; + + } + + JXMoveResizeWindow(display, np->window, x + xoffset, y + yoffset, + width, height); + + /* Reparent if this window likes to go other places. */ + if(np->needs_reparent) { + JXReparentWindow(display, np->window, dock->cp->window, + x + xoffset, y + yoffset); + } + + if(orientation == SYSTEM_TRAY_ORIENTATION_HORZ) { + x += itemWidth; + } else { + y += itemHeight; + } + } + +} + -- cgit v1.2.3