]> git.corax.cc Git - mwm/commitdiff
monitor: Implement statusbar and indicator bars
authorMatthias Kruk <m@m10k.eu>
Wed, 19 May 2021 00:02:56 +0000 (09:02 +0900)
committerMatthias Kruk <m@m10k.eu>
Wed, 19 May 2021 00:02:56 +0000 (09:02 +0900)
This implements the statusbar that indicates the active workspace
and displays the name of the root window (as status text).
This commit also implements the indicator bars, which highlight the
active/focused client (and will be used to display information about
clients in later versions).

monitor.c
monitor.h

index 9a618fc8f3b2f4657634fe545700f53eb820e76a..475b75780c55ec6b6ae3fd6ac83191e837679acc 100644 (file)
--- a/monitor.c
+++ b/monitor.c
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <X11/Xlib.h>
+#include "x.h"
+#include "mwm.h"
 #include "monitor.h"
+#include "workspace.h"
+#include "common.h"
+#include "loop.h"
+#include "client.h"
+#include "layout.h"
+
+#define STATUSBAR_HEIGHT 32
+#define INDICATOR_HEIGHT 32
+#define INDICATOR_PADDING 8
+
+#define HINDICATOR 0
+#define VINDICATOR 1
+
+struct indicator {
+       Window window;
+       struct geom geom;
+       GC gfx_context;
+       int orientation;
+};
 
 struct monitor {
        int id;
-       int x;
-       int y;
-       int w;
-       int h;
+       Window statusbar;
+       GC gfx_context;
+       XftDraw *xft_context;
+
+       struct indicator indicator[2];
+
+       int needs_redraw;
+       struct geom geom;
+       struct workspace *workspace;
+       struct layout *layout;
+       struct mwm *mwm;
 };
 
-int monitor_new(int id, int x, int y, int w, int h,
+extern struct layout *layouts[];
+
+static const char *_workspace_names[] = {
+       "い", "ろ", "は", "に", "ほ", "へ", "と", "ち", "り", "ぬ", "る", "を"
+};
+
+static void _indicator_update_window(struct indicator *indicator, struct monitor *monitor)
+{
+       if(indicator->window) {
+               XMoveResizeWindow(mwm_get_display(monitor->mwm), indicator->window,
+                                 indicator->geom.x, indicator->geom.y,
+                                 indicator->geom.w, indicator->geom.h);
+       } else {
+               indicator->window = mwm_create_window(monitor->mwm,
+                                                     indicator->geom.x, indicator->geom.y,
+                                                     indicator->geom.w, indicator->geom.h);
+               indicator->gfx_context = mwm_create_gc(monitor->mwm);
+               XMapRaised(mwm_get_display(monitor->mwm), indicator->window);
+       }
+
+       return;
+}
+
+static void _indicator_update_geometry(struct monitor *monitor)
+{
+       monitor->indicator[HINDICATOR].orientation = HINDICATOR;
+       monitor->indicator[HINDICATOR].geom.x = monitor->geom.x;
+       monitor->indicator[HINDICATOR].geom.y = monitor->geom.y + STATUSBAR_HEIGHT;
+       monitor->indicator[HINDICATOR].geom.w = monitor->geom.w;
+       monitor->indicator[HINDICATOR].geom.h = INDICATOR_HEIGHT;
+       _indicator_update_window(&monitor->indicator[HINDICATOR], monitor);
+
+       monitor->indicator[VINDICATOR].orientation = VINDICATOR;
+       monitor->indicator[VINDICATOR].geom.x = monitor->geom.x + monitor->geom.w - INDICATOR_HEIGHT;
+       monitor->indicator[VINDICATOR].geom.y = monitor->geom.y + STATUSBAR_HEIGHT;
+       monitor->indicator[VINDICATOR].geom.w = INDICATOR_HEIGHT;
+       monitor->indicator[VINDICATOR].geom.h = monitor->geom.h - STATUSBAR_HEIGHT;
+       _indicator_update_window(&monitor->indicator[VINDICATOR], monitor);
+
+       return;
+}
+
+void _indicator_set_visible(struct indicator *indicator, int visible, struct monitor *monitor)
+{
+       Display *display;
+
+       display = mwm_get_display(monitor->mwm);
+
+       if(visible) {
+               XMoveWindow(display, indicator->window, indicator->geom.x, indicator->geom.y);
+               /* XMapRaised(display, indicator->window); */
+       } else {
+               XMoveWindow(display, indicator->window, indicator->geom.w * -2, indicator->geom.y);
+               /* XUnmapWindow(display, indicator->window); */
+       }
+
+       return;
+}
+
+void _redraw_indicator(struct indicator *indicator, struct monitor *monitor)
+{
+       struct workspace *workspace;
+       struct client *focused;
+       Display *display;
+       Window root;
+       mwm_palette_t palette;
+
+       display = mwm_get_display(monitor->mwm);
+       root = mwm_get_root_window(monitor->mwm);
+
+       XCopyArea(display, root, indicator->window, indicator->gfx_context,
+                 indicator->geom.x, indicator->geom.y,
+                 indicator->geom.w, indicator->geom.h,
+                 0, 0);
+
+       workspace = monitor_get_workspace(monitor);
+       focused = workspace_get_focused_client(workspace);
+       palette = monitor_is_focused(monitor) ? MWM_PALETTE_ACTIVE : MWM_PALETTE_INACTIVE;
+
+       if(focused) {
+               struct geom focus_pos;
+               unsigned long fg_color;
+               unsigned long bg_color;
+
+               client_get_geometry(focused, &focus_pos);
+
+               fg_color = mwm_get_color(monitor->mwm, palette, MWM_COLOR_INDICATOR_FILL);
+               bg_color = mwm_get_color(monitor->mwm, palette, MWM_COLOR_INDICATOR_BORDER);
+
+               if(indicator->orientation == HINDICATOR) {
+                       focus_pos.x -= indicator->geom.x;
+                       focus_pos.y = INDICATOR_PADDING;
+                       focus_pos.h = INDICATOR_HEIGHT - 2 * INDICATOR_PADDING;
+               } else {
+                       focus_pos.x = INDICATOR_PADDING;
+                       focus_pos.y -= indicator->geom.y;
+                       focus_pos.w = INDICATOR_HEIGHT - 2 * INDICATOR_PADDING;
+               }
+
+               XSetForeground(display, indicator->gfx_context, fg_color);
+               XFillRectangle(display, indicator->window, indicator->gfx_context,
+                              focus_pos.x, focus_pos.y, focus_pos.w, focus_pos.h);
+               XSetForeground(display, indicator->gfx_context, bg_color);
+               XDrawRectangle(display, indicator->window, indicator->gfx_context,
+                              focus_pos.x, focus_pos.y, focus_pos.w, focus_pos.h);
+       }
+       /* XSync(display, False); ? */
+
+       return;
+}
+
+int monitor_redraw_indicators(struct monitor *monitor)
+{
+       layout_orientation_t orientation;
+
+       if(!monitor) {
+               return(-EINVAL);
+       }
+
+       orientation = layout_get_orientation(monitor->layout);
+
+       /* NOTE: LAYOUT_HORIZONTAL and LAYOUT_VERTICAL are *not* mutually exclusive */
+
+       if(orientation & LAYOUT_HORIZONTAL) {
+               _redraw_indicator(&monitor->indicator[HINDICATOR], monitor);
+               _indicator_set_visible(&monitor->indicator[HINDICATOR], 1, monitor);
+       } else {
+               _indicator_set_visible(&monitor->indicator[HINDICATOR], 0, monitor);
+       }
+
+       if(orientation & LAYOUT_VERTICAL) {
+               _redraw_indicator(&monitor->indicator[VINDICATOR], monitor);
+               _indicator_set_visible(&monitor->indicator[VINDICATOR], 1, monitor);
+       } else {
+               _indicator_set_visible(&monitor->indicator[VINDICATOR], 0, monitor);
+       }
+
+       return(0);
+}
+
+int monitor_new(struct mwm *mwm, int id, int x, int y, int w, int h,
                struct monitor **monitor)
 {
        struct monitor *mon;
@@ -26,11 +196,21 @@ int monitor_new(int id, int x, int y, int w, int h,
                return(-ENOMEM);
        }
 
+       memset(mon, 0, sizeof(*mon));
+
+       mon->mwm = mwm;
        mon->id = id;
-       mon->x = x;
-       mon->y = y;
-       mon->w = w;
-       mon->h = h;
+       mon->geom.x = x;
+       mon->geom.y = y;
+       mon->geom.w = w;
+       mon->geom.h = h;
+       mon->layout = layouts[0];
+
+       mon->statusbar = mwm_create_window(mwm, x, y, w, STATUSBAR_HEIGHT);
+       mon->gfx_context = mwm_create_gc(mwm);
+       mon->xft_context = mwm_create_xft_context(mwm, (Drawable)mon->statusbar);
+
+       _indicator_update_geometry(mon);
 
        *monitor = mon;
 
@@ -39,6 +219,8 @@ int monitor_new(int id, int x, int y, int w, int h,
 
 int monitor_free(struct monitor **monitor)
 {
+       Display *display;
+
        if(!monitor) {
                return(-EINVAL);
        }
@@ -47,12 +229,23 @@ int monitor_free(struct monitor **monitor)
                return(-EALREADY);
        }
 
+       display = mwm_get_display((*monitor)->mwm);
+
+       XUnmapWindow(display, (*monitor)->statusbar);
+       XDestroyWindow(display, (*monitor)->statusbar);
+       XFreeGC(display, (*monitor)->gfx_context);
+
        free(*monitor);
        *monitor = NULL;
 
        return(0);
 }
 
+Display* monitor_get_display(struct monitor *monitor)
+{
+       return(mwm_get_display(monitor->mwm));
+}
+
 int monitor_get_id(struct monitor *monitor)
 {
        if(!monitor) {
@@ -62,40 +255,347 @@ int monitor_get_id(struct monitor *monitor)
        return(monitor->id);
 }
 
-int monitor_get_geometry(struct monitor *monitor,
-                        int *x, int *y, int *w, int *h)
+int monitor_get_geometry(struct monitor *monitor, struct geom *geom)
+{
+       if(!monitor || !geom) {
+               return(-EINVAL);
+       }
+
+       memcpy(geom, &monitor->geom, sizeof(*geom));
+       return(0);
+}
+
+int monitor_set_geometry(struct monitor *monitor, struct geom *geom)
+{
+       if(!monitor || !geom) {
+               return(-EINVAL);
+       }
+
+       memcpy(&monitor->geom, geom, sizeof(*geom));
+
+       XMoveResizeWindow(mwm_get_display(monitor->mwm), monitor->statusbar,
+                         monitor->geom.x, monitor->geom.y,
+                         monitor->geom.w, STATUSBAR_HEIGHT);
+
+       return(0);
+}
+
+int monitor_swap_workspace(struct monitor *first, struct monitor *second)
+{
+       struct workspace *swap;
+
+       swap = first->workspace;
+       first->workspace = second->workspace;
+       second->workspace = swap;
+
+       workspace_set_viewer(first->workspace, first);
+       workspace_set_viewer(second->workspace, second);
+
+       workspace_needs_redraw(first->workspace);
+       workspace_needs_redraw(second->workspace);
+
+       return(0);
+}
+
+int monitor_set_workspace(struct monitor *monitor, struct workspace *workspace)
+{
+       struct monitor *other;
+
+       if(!monitor || !workspace) {
+               return(-EINVAL);
+       }
+
+       printf("%s(%p, %p)\n", __func__, (void*)monitor, (void*)workspace);
+       other = workspace_get_viewer(workspace);
+
+       if(other) {
+               return(monitor_swap_workspace(monitor, other));
+       } else {
+               struct workspace *old;
+
+               old = monitor_get_workspace(monitor);
+               workspace_set_viewer(old, NULL);
+               workspace_needs_redraw(old);
+       }
+
+       workspace_set_viewer(workspace, monitor);
+       monitor->workspace = workspace;
+       monitor_needs_redraw(monitor);
+
+       return(0);
+}
+
+struct workspace* monitor_get_workspace(struct monitor *monitor)
+{
+       return(monitor->workspace);
+}
+
+struct client* monitor_get_focused_client(struct monitor *monitor)
+{
+       struct workspace *workspace;
+
+       workspace = monitor_get_workspace(monitor);
+
+       if(!workspace) {
+               return(NULL);
+       }
+
+       return(workspace_get_focused_client(workspace));
+}
+
+int monitor_arrange_clients(struct monitor *monitor)
+{
+       struct geom geom;
+
+       if(!monitor) {
+               return(-EINVAL);
+       }
+
+       if(monitor_get_usable_area(monitor, &geom) < 0) {
+               return(-EFAULT);
+       }
+
+       layout_arrange(monitor->layout,
+                      monitor->workspace,
+                      &geom);
+
+       return(0);
+}
+
+int monitor_get_usable_area(struct monitor *monitor, struct geom *usable_area)
+{
+       layout_orientation_t orientation;
+
+       usable_area->x = monitor->geom.x;
+       usable_area->y = monitor->geom.y + STATUSBAR_HEIGHT;
+       usable_area->w = monitor->geom.w;
+       usable_area->h = monitor->geom.h - STATUSBAR_HEIGHT;
+
+       orientation = layout_get_orientation(monitor->layout);
+
+       if(orientation & LAYOUT_VERTICAL) {
+               usable_area->w -= INDICATOR_HEIGHT;
+       }
+
+       if(orientation & LAYOUT_HORIZONTAL) {
+               usable_area->y += INDICATOR_HEIGHT;
+               usable_area->h -= INDICATOR_HEIGHT;
+       }
+
+       return(0);
+}
+
+int _draw_client(struct workspace *workspace, struct client *client, void *data)
+{
+       client_show(client);
+       return(0);
+}
+
+int monitor_draw_clients(struct monitor *monitor)
+{
+       if(!monitor) {
+               return(-EINVAL);
+       }
+
+       workspace_foreach_client(monitor->workspace, _draw_client, monitor);
+
+       return(0);
+}
+
+int monitor_needs_redraw(struct monitor *monitor)
 {
        if(!monitor) {
                return(-EINVAL);
        }
 
-       if(x) {
-               *x = monitor->x;
+       monitor->needs_redraw = 1;
+       mwm_needs_redraw(monitor->mwm);
+
+       return(0);
+}
+
+struct _draw_workspace_data {
+       struct monitor *monitor;
+       mwm_palette_t palette;
+       Display *display;
+       int text_padding;
+       int text_width;
+       int i;
+       struct workspace *focused_workspace;
+};
+
+static int _draw_workspace_button(struct mwm *mwm, struct workspace *workspace, void *data)
+{
+       struct _draw_workspace_data *dwdata;
+       mwm_color_t color;
+       int button_width;
+       int focused;
+       int visible;
+       int x;
+
+       dwdata = (struct _draw_workspace_data*)data;
+
+       focused = workspace == dwdata->focused_workspace;
+       visible = workspace_get_viewer(workspace) != NULL;
+
+       button_width = dwdata->text_width + 2 * dwdata->text_padding;
+       x = dwdata->i * button_width;
+
+       if(focused) {
+               color = MWM_COLOR_FOCUSED;
+       } else if(visible) {
+               color = MWM_COLOR_VISIBLE;
+       } else {
+               color = MWM_COLOR_BACKGROUND;
+       }
+
+       XSetForeground(dwdata->display, dwdata->monitor->gfx_context,
+                      mwm_get_color(mwm, dwdata->palette, color));
+
+       XFillRectangle(dwdata->display, dwdata->monitor->statusbar,
+                      dwdata->monitor->gfx_context, x, 0,
+                      button_width, STATUSBAR_HEIGHT);
+
+       mwm_render_text(mwm, dwdata->monitor->xft_context, dwdata->palette,
+                       _workspace_names[dwdata->i], x + dwdata->text_padding, dwdata->text_padding);
+
+       /* A workspace necessarily has a focused client if it isn't empty */
+       if(workspace_get_focused_client(workspace)) {
+               XSetForeground(dwdata->display, dwdata->monitor->gfx_context,
+                              mwm_get_color(mwm, dwdata->palette, MWM_COLOR_CLIENT_INDICATOR));
+               XFillRectangle(dwdata->display, dwdata->monitor->statusbar,
+                              dwdata->monitor->gfx_context, x + 2, 2, button_width - 4, 2);
        }
-       if(y) {
-               *y = monitor->y;
+
+       dwdata->i++;
+
+       return(0);
+}
+
+static int _redraw_statusbar(struct monitor *monitor)
+{
+       struct _draw_workspace_data dwdata;
+       struct monitor *focused_monitor;
+       Display *display;
+       Window root;
+       int status_x;
+       int status_width;
+       int workspace_button_width;
+       char status[512];
+
+       if(!monitor) {
+               return(-EINVAL);
        }
-       if(w) {
-               *w = monitor->w;
+
+       display = mwm_get_display(monitor->mwm);
+       root = mwm_get_root_window(monitor->mwm);
+       focused_monitor = mwm_get_focused_monitor(monitor->mwm);
+       status[0] = 0;
+
+       /* start with background from root */
+       XCopyArea(display, root, monitor->statusbar,
+                 monitor->gfx_context,
+                 monitor->geom.x, monitor->geom.y,
+                 monitor->geom.w, STATUSBAR_HEIGHT,
+                 0, 0);
+
+       /* draw the workspace buttons */
+       dwdata.monitor = monitor;
+       dwdata.display = display;
+       dwdata.palette = focused_monitor == monitor ? MWM_PALETTE_ACTIVE : MWM_PALETTE_INACTIVE;
+       dwdata.text_padding = (STATUSBAR_HEIGHT - mwm_get_font_height(monitor->mwm)) / 2;
+       dwdata.text_width = mwm_get_text_width(monitor->mwm, _workspace_names[0]);
+       dwdata.i = 0;
+       dwdata.focused_workspace = monitor_get_workspace(monitor);
+
+       mwm_foreach_workspace(monitor->mwm, _draw_workspace_button, &dwdata);
+
+       workspace_button_width = dwdata.i * (dwdata.text_width + 2 * dwdata.text_padding);
+
+       mwm_get_status(monitor->mwm, status, sizeof(status));
+
+       /* right-align the status */
+       status_width = mwm_get_text_width(monitor->mwm, status) +
+               dwdata.text_padding * 2;
+       status_x = monitor->geom.w - status_width;
+
+       /*
+        * If there isn't enough space, left-align. I'd prefer part of the status to be cut
+        * off rather than drawing over the workspace buttons.
+        */
+       if(status_x < workspace_button_width) {
+               status_x = workspace_button_width;
+               status_width = monitor->geom.w - status_x;
        }
-       if(h) {
-               *h = monitor->h;
+
+       XSetForeground(display, monitor->gfx_context,
+                      mwm_get_color(monitor->mwm, dwdata.palette, MWM_COLOR_FOCUSED));
+       XFillRectangle(display, monitor->statusbar,
+                      monitor->gfx_context, status_x, 0,
+                      status_width, STATUSBAR_HEIGHT);
+       mwm_render_text(monitor->mwm, monitor->xft_context, dwdata.palette, status,
+                       status_x + dwdata.text_padding, dwdata.text_padding);
+
+       XMapRaised(display, monitor->statusbar);
+
+       return(0);
+}
+
+int monitor_redraw(struct monitor *monitor)
+{
+       monitor_arrange_clients(monitor);
+
+       if(monitor->needs_redraw) {
+               monitor_draw_clients(monitor);
+               /* monitor_draw(monitor); */
+               /* monitor_draw_bar() */
+
+               monitor->needs_redraw = 0;
        }
 
+       _redraw_statusbar(monitor);
+       monitor_redraw_indicators(monitor);
+
+       XSync(mwm_get_display(monitor->mwm), False);
+
        return(0);
 }
 
-int monitor_set_geometry(struct monitor *monitor,
-                        int x, int y, int w, int h)
+int monitor_set_layout(struct monitor *monitor,
+                      struct layout *layout)
 {
        if(!monitor) {
                return(-EINVAL);
        }
 
-       monitor->x = x;
-       monitor->y = y;
-       monitor->w = w;
-       monitor->h = h;
+       if(layout != monitor->layout) {
+               monitor->layout = layout;
+               monitor_needs_redraw(monitor);
+       }
 
        return(0);
 }
+
+struct layout* monitor_get_layout(struct monitor *monitor)
+{
+       return(monitor->layout);
+}
+
+int monitor_is_floating(struct monitor *monitor)
+{
+       if(!monitor) {
+               return(FALSE);
+       }
+
+       return(monitor->layout == NULL);
+}
+
+int monitor_is_dirty(struct monitor *monitor)
+{
+       return(monitor->needs_redraw);
+}
+
+int monitor_is_focused(struct monitor *monitor)
+{
+       return(mwm_get_focused_monitor(monitor->mwm) == monitor);
+}
index 04f32bd2b05c4da81e3c9fa6a91efae62dbd2b27..2165c96211d59a245b33e7b9d23324a747bb11db 100644 (file)
--- a/monitor.h
+++ b/monitor.h
@@ -1,16 +1,39 @@
 #ifndef MONITOR_H
 #define MONITOR_H 1
 
+#include <X11/Xlib.h>
+
+struct mwm;
 struct monitor;
+struct layout;
+struct workspace;
+struct geom;
 
-int monitor_new(int id, int x, int y, int w, int h,
+int monitor_new(struct mwm *mwm, int id, int x, int y, int w, int h,
                struct monitor **monitor);
 int monitor_free(struct monitor **monitor);
 
+Display* monitor_get_display(struct monitor *monitor);
+int monitor_set_layout(struct monitor *monitor,
+                      struct layout *layout);
+struct layout* monitor_get_layout(struct monitor *monitor);
+
 int monitor_get_id(struct monitor *monitor);
-int monitor_get_geometry(struct monitor *monitor,
-                        int *x, int *y, int *w, int *h);
-int monitor_set_geometry(struct monitor *monitor,
-                        int x, int y, int w, int h);
+
+int monitor_get_geometry(struct monitor *monitor, struct geom *geom);
+int monitor_set_geometry(struct monitor *monitor, struct geom *geom);
+int monitor_get_usable_area(struct monitor *monitor, struct geom *geom);
+
+int monitor_set_workspace(struct monitor *monitor, struct workspace *workspace);
+
+struct workspace* monitor_get_workspace(struct monitor *monitor);
+struct client* monitor_get_focused_client(struct monitor *monitor);
+
+int monitor_arrange_clients(struct monitor *monitor);
+int monitor_needs_redraw(struct monitor *monitor);
+int monitor_redraw(struct monitor *monitor);
+int monitor_is_floating(struct monitor *monitor);
+int monitor_is_dirty(struct monitor *monitor);
+int monitor_is_focused(struct monitor *monitor);
 
 #endif /* MONITOR_H */