#include <string.h>
#include <errno.h>
#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+#include <X11/Xproto.h>
#include <X11/extensions/Xinerama.h>
+#include <X11/Xft/Xft.h>
+#include <X11/XKBlib.h>
+#include <pango/pango.h>
+#include <pango/pangoxft.h>
#include "mwm.h"
+#include "keys.h"
#include "workspace.h"
#include "monitor.h"
-#include "array.h"
#include "loop.h"
#include "x.h"
#include "common.h"
+#include "client.h"
+#include "theme.h"
-typedef void (_mwm_xhandler_t)(struct mwm *, XEvent *);
+typedef void (_mwm_xhandler_t)(struct mwm*, XEvent*);
+
+#define FIND_MONITOR_BY_ID ((int(*)(void*, void*))_cmp_monitor_id)
+#define FIND_CLIENT_BY_WINDOW ((int(*)(struct client*, void*))_cmp_client_window)
+#define FIND_MONITOR_BY_WINDOW ((int(*)(void*, void*))_cmp_monitor_contains_window)
+#define FIND_MONITOR_BY_GEOM ((int(*)(void*, void*))_cmp_monitor_contains_geom)
+#define FIND_WORKSPACE_BY_VIEWER ((int(*)(void*, void*))_cmp_workspace_viewer)
+#define FIND_WORKSPACE_BY_NUMBER ((int(*)(void*, void*))_cmp_workspace_number)
+
+struct palette {
+ unsigned long color[MWM_COLOR_MAX];
+ XftColor xcolor[MWM_COLOR_MAX];
+};
struct mwm {
Display *display;
struct geom root_geom;
int running;
+ int needs_redraw;
struct loop *monitors;
struct loop *workspaces;
+ struct monitor *current_monitor;
+ struct client *focused_client;
_mwm_xhandler_t *xhandler[LASTEvent];
- mwm_handler_t *handler[MWM_EVENT_LAST];
+
+ struct {
+ PangoLayout *layout;
+ int ascent;
+ int descent;
+ int height;
+ } font;
+
+ struct palette palette[MWM_PALETTE_MAX];
+
+ void (*commands[MWM_CMD_MAX])(struct mwm*, void*);
+
+ int (*xerror_default_handler)(Display*, XErrorEvent*);
};
+extern struct mwm *__mwm;
+
+static int _xerror_startup(Display *display, XErrorEvent *event);
+static int _xerror_handle(Display *display, XErrorEvent *event);
+static int _xerror_nop(Display *display, XErrorEvent *event);
+
+static int _cmp_client_window(struct client *client, Window *window)
+{
+ return(client_get_window(client) == *window ? 0 : 1);
+}
+
+static int _cmp_monitor_id(struct monitor *mon, int *id)
+{
+ return(monitor_get_id(mon) == *id ? 0 : 1);
+}
+
+static int _cmp_monitor_contains_window(struct monitor *monitor, Window *window)
+{
+ struct geom window_geom;
+ struct geom monitor_geom;
+
+ if(x_get_geom(monitor_get_display(monitor), *window, &window_geom) < 0 ||
+ monitor_get_geometry(monitor, &monitor_geom) < 0) {
+ return(-EFAULT);
+ }
+
+ return(geom_intersects(&monitor_geom, &window_geom) > 0 ? 0 : 1);
+}
+
+static int _cmp_monitor_contains_geom(struct monitor *monitor, struct geom *geom)
+{
+ struct geom monitor_geom;
+
+ if(monitor_get_geometry(monitor, &monitor_geom) < 0) {
+ return(-EFAULT);
+ }
+
+ return(geom_intersects(&monitor_geom, geom) > 0 ? 0 : 1);
+}
+
+static int _cmp_workspace_viewer(struct workspace *workspace, struct monitor *monitor)
+{
+ return(workspace_get_viewer(workspace) == monitor ? 0 : 1);
+}
+
+static int _cmp_workspace_number(struct workspace *workspace, int *number)
+{
+ return(workspace_get_number(workspace) == *number ? 0 : 1);
+}
+
static void _mwm_button_press(struct mwm *mwm, XEvent *event)
{
+ XButtonPressedEvent *button_pressed;
+ struct monitor *event_monitor;
+ struct client *event_client;
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ button_pressed = &event->xbutton;
+
+ if(loop_find(&mwm->monitors, FIND_MONITOR_BY_WINDOW,
+ &button_pressed->window, (void**)&event_monitor) == 0) {
+ mwm_focus_monitor(mwm, event_monitor);
+ }
+
+ /*
+ * TODO: Handle the event
+ */
+
+ if(mwm_find_client(mwm, FIND_CLIENT_BY_WINDOW,
+ &button_pressed->window, &event_client) == 0) {
+ mwm_focus_client(mwm, event_client);
+ }
+
return;
}
static void _mwm_client_message(struct mwm *mwm, XEvent *event)
{
+ /* TODO: fullscreen toggle */
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
return;
}
static void _mwm_configure_request(struct mwm *mwm, XEvent *event)
{
- return;
-}
+ XConfigureRequestEvent *configure_request;
+ struct client *client;
-static int _cmp_monitor_id(struct monitor *mon, int *id)
-{
- return(monitor_get_id(mon) == *id);
+ /*
+ * This event is generated whenever the client attempts to resize itself.
+ * If the client is floating, or the viewer's layout is floating, we will
+ * accept the resize request.
+ * Otherwise we will override the request with the values that we have
+ * stored in the client structure.
+ */
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ configure_request = &event->xconfigurerequest;
+
+ if(mwm_find_client(mwm, FIND_CLIENT_BY_WINDOW,
+ &configure_request->window, &client) < 0) {
+ XWindowChanges changes;
+
+ /* no client associated with that window */
+
+ changes.x = configure_request->x;
+ changes.y = configure_request->y;
+ changes.width = configure_request->width;
+ changes.height = configure_request->height;
+ changes.border_width = 0; /* TODO: make border width configurable */
+ changes.sibling = configure_request->above;
+ changes.stack_mode = configure_request->detail;
+
+ XConfigureWindow(mwm->display, configure_request->window,
+ configure_request->value_mask, &changes);
+ } else {
+ struct geom requested_geom;
+
+ /*
+ * We have a client for that window. Let's see what it is
+ * that the client requested, and let client_change_geometry()
+ * do the deciding.
+ */
+
+ if(configure_request->value_mask & CWBorderWidth) {
+ client_set_border(client, configure_request->border_width);
+ }
+
+ client_get_geometry(client, &requested_geom);
+
+ if(configure_request->value_mask & CWX) {
+ requested_geom.x = configure_request->x;
+ }
+ if(configure_request->value_mask & CWY) {
+ requested_geom.y = configure_request->y;
+ }
+ if(configure_request->value_mask & CWWidth) {
+ requested_geom.w = configure_request->width;
+ }
+ if(configure_request->value_mask & CWHeight) {
+ requested_geom.h = configure_request->height;
+ }
+
+ client_change_geometry(client, &requested_geom);
+ }
+
+ XSync(mwm->display, False);
+
+ return;
}
static void _mwm_configure_notify(struct mwm *mwm, XEvent *event)
int num_monitors;
int i;
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
cevent = &event->xconfigure;
if(cevent->window != mwm->root) {
screen_info[i].x_org, screen_info[i].y_org,
screen_info[i].width, screen_info[i].height);
- if(loop_find(&mwm->monitors, (int(*)(void*,void*))_cmp_monitor_id,
+ if(loop_find(&mwm->monitors, FIND_MONITOR_BY_ID,
&screen_info[i].screen_number, (void**)&mon) < 0) {
- if(monitor_new(screen_info[i].screen_number,
+ if(monitor_new(mwm, screen_info[i].screen_number,
screen_info[i].x_org, screen_info[i].y_org,
screen_info[i].width, screen_info[i].height,
&mon) < 0) {
+ printf("Could not allocate monitor\n");
/* TODO: Let the user know */
return;
}
if(mwm_attach_monitor(mwm, mon) < 0) {
monitor_free(&mon);
/* TODO: Again, let the user know */
+ printf("Could not attach monitor\n");
return;
}
} else {
- /* update geometry */
+ struct geom new_geom;
- printf("Old monitor\n");
+ /* update geometry */
+ new_geom.x = screen_info[i].x_org;
+ new_geom.y = screen_info[i].y_org;
+ new_geom.w = screen_info[i].width;
+ new_geom.h = screen_info[i].height;
- if(monitor_set_geometry(mon, screen_info[i].x_org,
- screen_info[i].y_org,
- screen_info[i].width,
- screen_info[i].height) < 0) {
+ if(monitor_set_geometry(mon, &new_geom) < 0) {
/* TODO: Let the user know */
}
}
return;
}
-static void _mwm_destroy_notify(struct mwm *mwm, XEvent *event)
+static void _mwm_destroy_notify(struct mwm *mwm, XDestroyWindowEvent *event)
{
- return;
-}
+ struct client *client;
-static void _mwm_enter_notify(struct mwm *mwm, XEvent *event)
-{
+ /* get the client and detach it */
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ if(mwm_find_client(mwm, FIND_CLIENT_BY_WINDOW,
+ &event->window, &client) < 0) {
+ printf("Couldn't find client\n");
+ return;
+ }
+
+ if(mwm_detach_client(mwm, client) < 0) {
+ printf("Couldn't detach client\n");
+ return;
+ }
+
+ client_free(&client);
return;
}
-static void _mwm_expose(struct mwm *mwm, XEvent *event)
+static void _mwm_enter_notify(struct mwm *mwm, XCrossingEvent *event)
{
+ struct client *client;
+ struct monitor *monitor;
+
+ client = NULL;
+ monitor = NULL;
+
+ /* pointer has entered a window - move focus, if it makes sense */
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ if((event->mode != NotifyNormal || event->detail == NotifyInferior) &&
+ event->window == mwm->root) {
+ return;
+ }
+
+ if(mwm_find_client(mwm, FIND_CLIENT_BY_WINDOW,
+ &event->window, &client) == 0) {
+ mwm_focus_client(mwm, client);
+ }
+
+ if(loop_find(&mwm->monitors, FIND_MONITOR_BY_WINDOW,
+ &event->window, (void**)&monitor) == 0) {
+ mwm_focus_monitor(mwm, monitor);
+ }
+
return;
}
-static void _mwm_focus_in(struct mwm *mwm, XEvent *event)
+static void _mwm_expose(struct mwm *mwm, XExposeEvent *event)
{
+ struct monitor *monitor;
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ /* redraw the status bar, if we have one */
+
+ if(event->count > 0) {
+ return;
+ }
+
+ if(loop_find(&mwm->monitors, FIND_MONITOR_BY_WINDOW,
+ &event->window, (void**)&monitor) == 0) {
+ monitor_needs_redraw(monitor);
+ }
+
return;
}
-static void _mwm_key_press(struct mwm *mwm, XEvent *event)
+static void _mwm_focus_in(struct mwm *mwm, XFocusInEvent *event)
{
+ struct client *client;
+
+ /* move focus to the client referenced by the event */
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ if(mwm_find_client(mwm, FIND_CLIENT_BY_WINDOW,
+ &event->window, &client) < 0) {
+ return;
+ }
+
+ mwm_focus_client(mwm, client);
+
return;
}
-static void _mwm_mapping_notify(struct mwm *mwm, XEvent *event)
+static void _mwm_key_press(struct mwm *mwm, XKeyEvent *event)
{
+ extern struct key_binding config_keybindings[];
+ struct key_binding *binding;
+ KeySym keysym;
+ unsigned int mask;
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
+#define ALLMODMASK (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
+#define ALLMASK (ShiftMask | ControlMask | ALLMODMASK)
+#define CLEANMASK(mask) (mask & ~LockMask & ALLMASK)
+
+ /* handle keyboard shortcuts */
+
+ keysym = XkbKeycodeToKeysym(mwm->display, event->keycode, 0, 0);
+ mask = CLEANMASK(event->state);
+
+ for(binding = config_keybindings; binding->cmd < MWM_CMD_MAX; binding++) {
+ if(keysym == binding->key && mask == CLEANMASK(binding->mod)) {
+ mwm_cmd(mwm, binding->cmd, binding->arg);
+ }
+ }
+
+#undef BUTTONMASK
+#undef ALLMODMASK
+#undef ALLMASK
+#undef CLEANMASK
return;
}
-static void _mwm_map_request(struct mwm *mwm, XEvent *event)
+static void _mwm_mapping_notify(struct mwm *mwm, XMappingEvent *event)
{
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ XRefreshKeyboardMapping(event);
+
+ if(event->request == MappingKeyboard) {
+ mwm_grab_keys(mwm);
+ }
+
return;
}
-static void _mwm_motion_notify(struct mwm *mwm, XEvent *event)
+static void _mwm_map_request(struct mwm *mwm, XMapRequestEvent *event)
{
+ XWindowAttributes attrs;
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ if(!XGetWindowAttributes(mwm->display, event->window, &attrs)) {
+ return;
+ }
+
+ if(attrs.override_redirect) {
+ return;
+ }
+
+ if(mwm_find_client(mwm, FIND_CLIENT_BY_WINDOW,
+ &event->window, NULL) < 0) {
+ struct client *client;
+
+ if(client_new(event->window, &attrs, &client) < 0) {
+ /* ENOMEM */
+ return;
+ }
+
+ if(mwm_attach_client(mwm, client) < 0) {
+ /* ENOMEM */
+ client_free(&client);
+ return;
+ }
+ }
+
return;
}
-static void _mwm_property_notify(struct mwm *mwm, XEvent *event)
+static void _mwm_motion_notify(struct mwm *mwm, XMotionEvent *event)
{
+ struct monitor *monitor;
+ struct geom pointer_geom;
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ /* move focus to the monitor referenced in the event */
+ /* printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event); */
+
+ pointer_geom.x = event->x_root;
+ pointer_geom.y = event->y_root;
+ pointer_geom.w = 1;
+ pointer_geom.h = 1;
+
+ if(loop_find(&mwm->monitors, FIND_MONITOR_BY_GEOM,
+ &pointer_geom, (void**)&monitor) < 0) {
+ return;
+ }
+
+ if(mwm_get_focused_monitor(mwm) != monitor) {
+ mwm_focus_monitor(mwm, monitor);
+ }
+
return;
}
-static void _mwm_unmap_notify(struct mwm *mwm, XEvent *event)
+static void _mwm_property_notify(struct mwm *mwm, XPropertyEvent *event)
{
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ /* FIXME: Property notification handling must be implemented more thoroughly */
+
+ if((event->window == mwm->root)) {
+ /* if(event->atom == XA_WM_NAME) */
+ mwm_needs_redraw(mwm);
+ }
+
return;
}
-static void _mwm_notify(struct mwm *mwm, mwm_event_t event, void *data)
+static void _mwm_unmap_notify(struct mwm *mwm, XUnmapEvent *event)
{
- if(!mwm || event >= MWM_EVENT_LAST) {
+ struct client *client;
+
+ printf("%s(%p, %p)\n", __func__, (void*)mwm, (void*)event);
+
+ if(mwm_find_client(mwm, FIND_CLIENT_BY_WINDOW,
+ &event->window, &client) < 0) {
return;
}
- if(mwm->handler[event]) {
- mwm->handler[event](mwm, event, data);
+ XGrabServer(mwm->display);
+ XSetErrorHandler(_xerror_nop);
+
+ client_set_state(client, WithdrawnState);
+
+ if(!event->send_event) {
+ mwm_detach_client(mwm, client);
+ client_free(&client);
}
+ XSync(mwm->display, False);
+ XSetErrorHandler(_xerror_handle);
+ XUngrabServer(mwm->display);
+
return;
}
}
}
- mwm->xhandler[ButtonPress] = _mwm_button_press;
- mwm->xhandler[ClientMessage] = _mwm_client_message;
- mwm->xhandler[ConfigureRequest] = _mwm_configure_request;
- mwm->xhandler[ConfigureNotify] = _mwm_configure_notify;
- mwm->xhandler[DestroyNotify] = _mwm_destroy_notify;
- mwm->xhandler[EnterNotify] = _mwm_enter_notify;
- mwm->xhandler[Expose] = _mwm_expose;
- mwm->xhandler[FocusIn] = _mwm_focus_in;
- mwm->xhandler[KeyPress] = _mwm_key_press;
- mwm->xhandler[MappingNotify] = _mwm_mapping_notify;
- mwm->xhandler[MapRequest] = _mwm_map_request;
- mwm->xhandler[MotionNotify] = _mwm_motion_notify;
- mwm->xhandler[PropertyNotify] = _mwm_property_notify;
- mwm->xhandler[UnmapNotify] = _mwm_unmap_notify;
+ mwm->xhandler[ButtonPress] = (_mwm_xhandler_t*)_mwm_button_press;
+ mwm->xhandler[ClientMessage] = (_mwm_xhandler_t*)_mwm_client_message;
+ mwm->xhandler[ConfigureRequest] = (_mwm_xhandler_t*)_mwm_configure_request;
+ mwm->xhandler[ConfigureNotify] = (_mwm_xhandler_t*)_mwm_configure_notify;
+ mwm->xhandler[DestroyNotify] = (_mwm_xhandler_t*)_mwm_destroy_notify;
+ mwm->xhandler[EnterNotify] = (_mwm_xhandler_t*)_mwm_enter_notify;
+ mwm->xhandler[Expose] = (_mwm_xhandler_t*)_mwm_expose;
+ mwm->xhandler[FocusIn] = (_mwm_xhandler_t*)_mwm_focus_in;
+ mwm->xhandler[KeyPress] = (_mwm_xhandler_t*)_mwm_key_press;
+ mwm->xhandler[MappingNotify] = (_mwm_xhandler_t*)_mwm_mapping_notify;
+ mwm->xhandler[MapRequest] = (_mwm_xhandler_t*)_mwm_map_request;
+ mwm->xhandler[MotionNotify] = (_mwm_xhandler_t*)_mwm_motion_notify;
+ mwm->xhandler[PropertyNotify] = (_mwm_xhandler_t*)_mwm_property_notify;
+ mwm->xhandler[UnmapNotify] = (_mwm_xhandler_t*)_mwm_unmap_notify;
cleanup:
if(err < 0) {
return(0);
}
-int mwm_init(struct mwm *mwm)
+Display* mwm_get_display(struct mwm *mwm)
{
- if(!mwm) {
- return(-EINVAL);
- }
+ return(mwm->display);
+}
- mwm->display = XOpenDisplay(NULL);
+Window mwm_get_root_window(struct mwm *mwm)
+{
+ return(mwm->root);
+}
- if(!mwm->display) {
+static int _color_init(struct mwm *mwm,
+ unsigned long *color,
+ XftColor *xcolor,
+ const char *colorspec)
+{
+ Visual *visual;
+ Colormap colormap;
+
+ visual = DefaultVisual(mwm->display, mwm->screen);
+ colormap = DefaultColormap(mwm->display, mwm->screen);
+
+ if(!XftColorAllocName(mwm->display, visual, colormap,
+ colorspec, xcolor)) {
return(-EIO);
}
- mwm->screen = DefaultScreen(mwm->display);
- mwm->root = RootWindow(mwm->display, mwm->screen);
+ *color = xcolor->pixel;
+ return(0);
+}
- XSelectInput(mwm->display, mwm->root,
- SubstructureRedirectMask |
- SubstructureNotifyMask |
- ButtonPressMask |
- PointerMotionMask |
- EnterWindowMask |
- LeaveWindowMask |
- StructureNotifyMask |
- PropertyChangeMask);
+static int _palette_init(struct mwm *mwm,
+ struct palette *palette,
+ union colorset *colorset)
+{
+ int i;
- x_configure_notify(mwm->display, mwm->root, NULL, 0);
+ for(i = 0; i < MWM_COLOR_MAX; i++) {
+ _color_init(mwm, &palette->color[i],
+ &palette->xcolor[i],
+ colorset->indexed[i]);
+ }
return(0);
}
-int mwm_run(struct mwm *mwm)
+void _sigchld(int unused)
{
- XEvent event;
+ if(signal(SIGCHLD, _sigchld) == SIG_ERR) {
+ perror("signal");
+ exit(1);
+ }
- XSync(mwm->display, False);
- mwm->running = 1;
+ while(waitpid(-1, NULL, WNOHANG) > 0);
- while(mwm->running && !XNextEvent(mwm->display, &event)) {
- if(mwm->xhandler[event.type]) {
- mwm->xhandler[event.type](mwm, &event);
- }
+ return;
+}
+
+static void _cmd_spawn(struct mwm *mwm, void *arg)
+{
+ char **argv;
+ pid_t pid;
+
+ argv = (char**)arg;
+ pid = fork();
+
+ if(pid == 0) {
+ close(ConnectionNumber(mwm->display));
+ setsid();
+ execvp(*argv, argv);
+ exit(0);
}
- return(0);
+ return;
}
-int mwm_stop(struct mwm *mwm)
+static void _cmd_show_workspace(struct mwm *mwm, void *arg)
{
- if(!mwm) {
- return(-EINVAL);
+ struct monitor *monitor;
+ struct workspace *workspace;
+ long number;
+
+ number = (long)arg;
+ monitor = mwm_get_focused_monitor(mwm);
+
+ if(loop_find(&mwm->workspaces, FIND_WORKSPACE_BY_NUMBER,
+ (void*)&number, (void**)&workspace) < 0) {
+ return;
}
- if(!mwm->running) {
- return(-EALREADY);
+ monitor_set_workspace(monitor, workspace);
+ return;
+}
+
+static void _cmd_move_to_workspace(struct mwm *mwm, void *arg)
+{
+ struct workspace *src_workspace;
+ struct workspace *dst_workspace;
+ struct client *client;
+ long dst_number;
+
+ dst_number = (long)arg;
+ client = mwm_get_focused_client(mwm);
+
+ if(!client) {
+ return;
}
- mwm->running = 0;
+ if(loop_find(&mwm->workspaces, FIND_WORKSPACE_BY_NUMBER,
+ (void*)&dst_number, (void**)&dst_workspace) < 0) {
+ return;
+ }
- return(0);
+ src_workspace = client_get_workspace(client);
+
+ if(src_workspace != dst_workspace) {
+ workspace_detach_client(src_workspace, client);
+ workspace_attach_client(dst_workspace, client);
+ }
+
+ return;
}
-int mwm_attach_monitor(struct mwm *mwm, struct monitor *mon)
+static void _cmd_set_layout(struct mwm *mwm, void *arg)
{
- int idx;
+ extern struct layout *layouts[];
+ struct monitor *monitor;
+ long num;
- if(!mwm || !mon) {
- return(-EINVAL);
+ num = (long)arg;
+ monitor = mwm_get_focused_monitor(mwm);
+
+ if(monitor_get_layout(monitor) != layouts[num]) {
+ monitor_set_layout(monitor, layouts[num]);
+ monitor_needs_redraw(monitor);
}
- idx = monitor_get_id(mon);
+ return;
+}
- if(loop_append(&mwm->monitors, mon) < 0) {
- return(-ENOMEM);
- }
+static void _cmd_shift_focus(struct mwm *mwm, void *arg)
+{
+ struct workspace *workspace;
+ long dir;
- _mwm_notify(mwm, MWM_EVENT_MONITOR_ATTACHED, mon);
+ dir = (long)arg;
- printf("Attached monitor %d: %p\n", idx, (void*)mon);
+ workspace = mwm_get_focused_workspace(mwm);
+ workspace_shift_focus(workspace, dir);
- return(0);
+ return;
}
-int mwm_detach_monitor(struct mwm *mwm, struct monitor *mon)
+static void _cmd_shift_client(struct mwm *mwm, void *arg)
{
- if(!mwm || !mon) {
- return(-EINVAL);
+ struct workspace *workspace;
+ long dir;
+
+ dir = (long)arg;
+
+ workspace = mwm_get_focused_workspace(mwm);
+ workspace_shift_client(workspace, NULL, dir);
+
+ return;
+}
+
+static void _cmd_shift_monitor_focus(struct mwm *mwm, void *arg)
+{
+ struct monitor *src_monitor;
+ struct monitor *dst_monitor;
+ long dir;
+
+ dir = (long)arg;
+ /* move focus to previous or next monitor */
+
+ if(!mwm || dir == 0) {
+ return;
}
- if(loop_remove(&mwm->monitors, mon) < 0) {
- return(-ENODEV);
+ src_monitor = mwm_get_focused_monitor(mwm);
+
+ if(dir > 0) {
+ if(loop_get_next(&mwm->monitors, src_monitor, (void**)&dst_monitor) < 0) {
+ return;
+ }
+ } else {
+ if(loop_get_prev(&mwm->monitors, src_monitor, (void**)&dst_monitor) < 0) {
+ return;
+ }
+ }
+
+ if(src_monitor != dst_monitor) {
+ mwm_focus_monitor(mwm, dst_monitor);
+
+ monitor_needs_redraw(src_monitor);
+ monitor_needs_redraw(dst_monitor);
+ }
+
+ return;
+}
+
+static void _cmd_shift_workspace(struct mwm *mwm, void *arg)
+{
+ struct monitor *src_monitor;
+ struct monitor *dst_monitor;
+ struct workspace *workspace;
+ long dir;
+
+ /* move workspace to the next or previous monitor */
+
+ dir = (long)arg;
+
+ if(!mwm || dir == 0) {
+ return;
+ }
+
+ src_monitor = mwm_get_focused_monitor(mwm);
+ workspace = monitor_get_workspace(src_monitor);
+
+ if(dir > 0) {
+ if(loop_get_next(&mwm->monitors, src_monitor, (void**)&dst_monitor) < 0) {
+ return;
+ }
+ } else {
+ if(loop_get_prev(&mwm->monitors, src_monitor, (void**)&dst_monitor) < 0) {
+ return;
+ }
+ }
+
+ if(src_monitor != dst_monitor) {
+ monitor_set_workspace(dst_monitor, workspace);
+ mwm_focus_monitor(mwm, dst_monitor);
+ }
+
+ return;
+}
+
+static void _cmd_quit(struct mwm *mwm, void *arg)
+{
+ mwm_stop(mwm);
+ return;
+}
+
+static int _xerror_startup(Display *display, XErrorEvent *event)
+{
+ fprintf(stderr, "Looks like I'm not your only window manager\n");
+ exit(1);
+ return(-1);
+}
+
+static int _xerror_nop(Display *display, XErrorEvent *event)
+{
+ return(0);
+}
+
+static int _can_ignore_error(XErrorEvent *event)
+{
+ static struct {
+ unsigned char error_code;
+ unsigned char request_code;
+ } ignore_ok[] = {
+ { BadMatch, X_SetInputFocus },
+ { BadDrawable, X_PolyText8 },
+ { BadDrawable, X_PolyFillRectangle },
+ { BadDrawable, X_PolySegment },
+ { BadMatch, X_ConfigureWindow },
+ { BadAccess, X_GrabButton },
+ { BadAccess, X_GrabKey },
+ { BadDrawable, X_CopyArea }
+ };
+ int i;
+
+ for(i = 0; i < (sizeof(ignore_ok) / sizeof(ignore_ok[0])); i++) {
+ if(event->request_code == ignore_ok[i].request_code &&
+ event->error_code == ignore_ok[i].error_code) {
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+static int _xerror_handle(Display *display, XErrorEvent *event)
+{
+ if(_can_ignore_error(event)) {
+ return(0);
+ }
+
+ return(__mwm->xerror_default_handler(display, event));
+}
+
+int mwm_init(struct mwm *mwm)
+{
+ extern struct theme config_theme;
+ PangoFontMap *fontmap;
+ PangoContext *context;
+ PangoFontDescription *fontdesc;
+ PangoFontMetrics *fontmetrics;
+
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ mwm->display = XOpenDisplay(NULL);
+
+ if(!mwm->display) {
+ return(-EIO);
+ }
+
+ _sigchld(0);
+
+ mwm->screen = DefaultScreen(mwm->display);
+ mwm->root = RootWindow(mwm->display, mwm->screen);
+ mwm->xerror_default_handler = XSetErrorHandler(_xerror_startup);
+
+ if(!mwm->xerror_default_handler) {
+ return(-EIO);
+ }
+
+ XSelectInput(mwm->display, mwm->root,
+ SubstructureRedirectMask |
+ SubstructureNotifyMask |
+ /* ButtonPressMask | */
+ PointerMotionMask |
+ EnterWindowMask |
+ LeaveWindowMask |
+ StructureNotifyMask |
+ PropertyChangeMask);
+ XSync(mwm->display, False);
+
+ XSetErrorHandler(_xerror_handle);
+ XSync(mwm->display, False);
+
+ mwm_grab_keys(mwm);
+
+ x_configure_notify(mwm->display, mwm->root, NULL, 0);
+
+ fontmap = pango_xft_get_font_map(mwm->display, mwm->screen);
+ context = pango_font_map_create_context(fontmap);
+ fontdesc = pango_font_description_from_string(config_theme.statusbar_font);
+ mwm->font.layout = pango_layout_new(context);
+ pango_layout_set_font_description(mwm->font.layout, fontdesc);
+
+ fontmetrics = pango_context_get_metrics(context, fontdesc, NULL);
+ mwm->font.ascent = pango_font_metrics_get_ascent(fontmetrics) / PANGO_SCALE;
+ mwm->font.descent = pango_font_metrics_get_descent(fontmetrics) / PANGO_SCALE;
+ mwm->font.height = mwm->font.ascent + mwm->font.descent;
+
+ _palette_init(mwm, &(mwm->palette[MWM_PALETTE_ACTIVE]),
+ &config_theme.active);
+ _palette_init(mwm, &(mwm->palette[MWM_PALETTE_INACTIVE]),
+ &config_theme.inactive);
+
+ pango_font_metrics_unref(fontmetrics);
+ g_object_unref(context);
+
+ mwm->commands[MWM_CMD_QUIT] = _cmd_quit;
+ mwm->commands[MWM_CMD_SPAWN] = _cmd_spawn;
+ mwm->commands[MWM_CMD_SHOW_WORKSPACE] = _cmd_show_workspace;
+ mwm->commands[MWM_CMD_MOVE_TO_WORKSPACE] = _cmd_move_to_workspace;
+ mwm->commands[MWM_CMD_SET_LAYOUT] = _cmd_set_layout;
+ mwm->commands[MWM_CMD_SHIFT_FOCUS] = _cmd_shift_focus;
+ mwm->commands[MWM_CMD_SHIFT_CLIENT] = _cmd_shift_client;
+ mwm->commands[MWM_CMD_SHIFT_MONITOR_FOCUS] = _cmd_shift_monitor_focus;
+ mwm->commands[MWM_CMD_SHIFT_WORKSPACE] = _cmd_shift_workspace;
+
+ return(0);
+}
+
+int mwm_render_text(struct mwm *mwm, XftDraw *drawable,
+ mwm_palette_t palette, const char *text,
+ const int x, const int y)
+{
+ XftColor *color;
+
+ if(!mwm || !drawable || !text) {
+ return(-EINVAL);
+ }
+
+ color = &mwm->palette[palette].xcolor[MWM_COLOR_TEXT];
+
+ pango_layout_set_attributes(mwm->font.layout, NULL);
+
+ pango_layout_set_markup(mwm->font.layout, text, -1);
+ pango_xft_render_layout(drawable, color,
+ mwm->font.layout,
+ x * PANGO_SCALE,
+ y * PANGO_SCALE);
+
+ return(0);
+}
+
+int mwm_run(struct mwm *mwm)
+{
+ XEvent event;
+
+ XSync(mwm->display, False);
+ mwm->running = 1;
+
+ while(mwm->running && !XNextEvent(mwm->display, &event)) {
+ struct client *focused_client;
+
+ if(mwm->xhandler[event.type]) {
+ /* printf("XEvent: %x\n", event.type); */
+ mwm->xhandler[event.type](mwm, &event);
+ }
+
+ if(mwm->needs_redraw) {
+ mwm_redraw(mwm);
+ }
+
+ focused_client = mwm_get_focused_client(mwm);
+
+ if(mwm->focused_client != focused_client) {
+ client_focus(focused_client);
+ mwm->focused_client = focused_client;
+ }
+ }
+
+ return(0);
+}
+
+int mwm_stop(struct mwm *mwm)
+{
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ if(!mwm->running) {
+ return(-EALREADY);
+ }
+
+ mwm->running = 0;
+
+ return(0);
+}
+
+int mwm_attach_monitor(struct mwm *mwm, struct monitor *mon)
+{
+ struct workspace *unviewed;
+
+ if(!mwm || !mon) {
+ return(-EINVAL);
+ }
+
+ if(loop_append(&mwm->monitors, mon) < 0) {
+ return(-ENOMEM);
+ }
+
+ if(!mwm_get_focused_monitor(mwm)) {
+ mwm_focus_monitor(mwm, mon);
+ }
+
+ if(loop_find(&mwm->workspaces, FIND_WORKSPACE_BY_VIEWER, NULL, (void**)&unviewed) < 0) {
+ return(-EFAULT);
+ }
+
+ monitor_set_workspace(mon, unviewed);
+
+ return(0);
+}
+
+int mwm_detach_monitor(struct mwm *mwm, struct monitor *mon)
+{
+ struct workspace *workspace;
+
+ if(!mwm || !mon) {
+ return(-EINVAL);
+ }
+
+ if(loop_remove(&mwm->monitors, mon) < 0) {
+ return(-ENODEV);
+ }
+
+ workspace = monitor_get_workspace(mon);
+ workspace_set_viewer(workspace, NULL);
+
+ return(0);
+}
+
+int mwm_focus_monitor(struct mwm *mwm, struct monitor *monitor)
+{
+ if(!mwm || !monitor) {
+ return(-EINVAL);
+ }
+
+ if(mwm->current_monitor != monitor) {
+#if MWM_DEBUG
+ printf("New current monitor: %p\n", (void*)monitor);
+#endif /* MWM_DEBUG */
+
+ monitor_needs_redraw(mwm->current_monitor);
+ monitor_needs_redraw(monitor);
+ }
+
+ mwm->current_monitor = monitor;
+
+ return(0);
+}
+
+struct monitor* mwm_get_focused_monitor(struct mwm *mwm)
+{
+ return(mwm->current_monitor);
+}
+
+int mwm_attach_client(struct mwm *mwm, struct client *client)
+{
+ struct workspace *workspace;
+
+ if(!mwm || !client) {
+ return(-EINVAL);
+ }
+
+ workspace = NULL;
+
+ if(mwm->current_monitor) {
+ workspace = monitor_get_workspace(mwm->current_monitor);
+ }
+
+ if(!workspace) {
+ /*
+ * there's a chance that we might be attaching clients
+ * before the first monitor has been detected
+ */
+
+ if(loop_get_first(&mwm->workspaces, (void**)&workspace) < 0) {
+ /* this really shouldn't happen */
+ return(-EFAULT);
+ }
+ }
+
+#if MWM_DEBUG
+ printf("Attaching client %p to workspace %p\n",
+ (void*)client, (void*)workspace);
+#endif /* MWM_DEBUG */
+
+ return(workspace_attach_client(workspace, client));
+}
+
+int mwm_detach_client(struct mwm *mwm, struct client *client)
+{
+ struct workspace *workspace;
+
+ if(!mwm || !client) {
+ return(-EINVAL);
+ }
+
+ workspace = client_get_workspace(client);
+
+ workspace_detach_client(workspace, client);
+
+ return(0);
+}
+
+int mwm_focus_client(struct mwm *mwm, struct client *client)
+{
+ struct workspace *workspace;
+
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ if(client) {
+ workspace = client_get_workspace(client);
+ } else {
+ workspace = mwm_get_focused_workspace(mwm);
+ }
+
+ if(!workspace) {
+ return(-EBADFD);
+ }
+
+ return(workspace_focus_client(workspace, client));
+}
+
+struct client* mwm_get_focused_client(struct mwm *mwm)
+{
+ struct monitor *focused_monitor;
+
+ focused_monitor = mwm_get_focused_monitor(mwm);
+
+ if(!focused_monitor) {
+ return(NULL);
+ }
+
+ return(monitor_get_focused_client(focused_monitor));
+}
+
+int mwm_find_client(struct mwm *mwm, int(*cmp)(struct client*, void*),
+ void *data, struct client **client)
+{
+ loop_iter_t first;
+ loop_iter_t cur;
+
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ first = loop_get_iter(&mwm->workspaces);
+ cur = first;
+
+ do {
+ struct workspace *workspace;
+
+ workspace = (struct workspace*)loop_iter_get_data(cur);
+
+ if(!workspace) {
+ fprintf(stderr, "%s: Invalid workspace in loop\n", __func__);
+ continue;
+ }
+
+ if(workspace_find_client(workspace, cmp, data, client) == 0) {
+ return(0);
+ }
+
+ cur = loop_iter_get_next(cur);
+ } while(cur != first);
+
+ return(-ENOENT);
+}
+
+struct workspace *mwm_get_focused_workspace(struct mwm *mwm)
+{
+ struct monitor *monitor;
+ struct workspace *workspace;
+
+ workspace = NULL;
+ monitor = mwm_get_focused_monitor(mwm);
+
+ if(monitor) {
+ workspace = monitor_get_workspace(monitor);
+ }
+
+ return(workspace);
+}
+
+int mwm_foreach_workspace(struct mwm *mwm,
+ int (*func)(struct mwm*, struct workspace*, void*),
+ void *data)
+{
+ loop_iter_t first;
+ loop_iter_t cur;
+
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ first = loop_get_iter(&mwm->workspaces);
+ cur = first;
+
+ do {
+ struct workspace *workspace;
+
+ workspace = (struct workspace*)loop_iter_get_data(cur);
+
+ if(func(mwm, workspace, data) < 0) {
+ break;
+ }
+ cur = loop_iter_get_next(cur);
+ } while(cur != first);
+
+ return(0);
+}
+
+int mwm_needs_redraw(struct mwm *mwm)
+{
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ mwm->needs_redraw = 1;
+ return(0);
+}
+
+int mwm_redraw(struct mwm *mwm)
+{
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ if(mwm->needs_redraw) {
+ loop_foreach(&mwm->monitors, (void(*)(void*))monitor_redraw);
+ loop_foreach(&mwm->workspaces, (void(*)(void*))workspace_redraw);
+ mwm->needs_redraw = 0;
+ }
+
+ return(0);
+}
+
+Window mwm_create_window(struct mwm *mwm, const int x, const int y, const int w, const int h)
+{
+ Window window;
+ XSetWindowAttributes attrs;
+ int depth;
+ Visual *visual;
+ unsigned long mask;
+
+ attrs.override_redirect = True;
+ attrs.background_pixmap = ParentRelative;
+ attrs.event_mask = ExposureMask;
+
+ mask = CWOverrideRedirect | CWBackPixmap | CWEventMask;
+ depth = DefaultDepth(mwm->display, mwm->screen);
+ visual = DefaultVisual(mwm->display, mwm->screen);
+
+ window = XCreateWindow(mwm->display, mwm->root, x, y, w, h, 0,
+ depth, CopyFromParent, visual, mask, &attrs);
+
+ return(window);
+}
+
+GC mwm_create_gc(struct mwm *mwm)
+{
+ GC context;
+
+ context = XCreateGC(mwm->display, mwm->root, 0, NULL);
+
+ XSetLineAttributes(mwm->display, context, 1, LineSolid, CapButt, JoinMiter);
+
+ return(context);
+}
+
+XftDraw* mwm_create_xft_context(struct mwm *mwm, Drawable drawable)
+{
+ return(XftDrawCreate(mwm->display, drawable,
+ DefaultVisual(mwm->display, mwm->screen),
+ DefaultColormap(mwm->display, mwm->screen)));
+}
+
+int mwm_get_font_height(struct mwm *mwm)
+{
+ return(mwm->font.height);
+}
+
+int mwm_get_text_width(struct mwm *mwm, const char *text)
+{
+ PangoRectangle extents;
+
+ pango_layout_set_attributes(mwm->font.layout, NULL);
+ pango_layout_set_markup(mwm->font.layout, text, -1);
+ pango_layout_get_extents(mwm->font.layout, 0, &extents);
+
+ return(extents.width / PANGO_SCALE);
+}
+
+unsigned long mwm_get_color(struct mwm *mwm, mwm_palette_t palette, mwm_color_t color)
+{
+ return(mwm->palette[palette].color[color]);
+}
+
+int mwm_get_text_property(struct mwm *mwm, Window window, Atom atom, char *buffer, size_t buffer_size)
+{
+ XTextProperty property;
+ int len;
+
+ if(!mwm || !buffer || buffer_size == 0) {
+ return(-EINVAL);
+ }
+
+ XGetTextProperty(mwm->display, window, &property, atom);
+
+ if(property.nitems == 0) {
+ return(-ENOENT);
+ }
+
+ if(property.encoding == XA_STRING) {
+ len = snprintf(buffer, buffer_size, "%s", (char*)property.value);
+ } else {
+ len = -ENOSYS;
+ }
+
+ XFree(property.value);
+ return(len);
+}
+
+int mwm_get_status(struct mwm *mwm, char *buffer, const size_t buffer_size)
+{
+ int len;
+
+ len = mwm_get_text_property(mwm, mwm->root, XA_WM_NAME, buffer, buffer_size);
+
+ if(len < 0) {
+ return(snprintf(buffer, buffer_size, "mwm-0.1"));
+ }
+
+ return(len);
+}
+
+int mwm_grab_keys(struct mwm *mwm)
+{
+ extern struct key_binding config_keybindings[];
+ struct key_binding *binding;
+
+ if(!mwm) {
+ return(-EINVAL);
+ }
+
+ XUngrabKey(mwm->display, AnyKey, AnyModifier, mwm->root);
+
+ for(binding = config_keybindings; binding->cmd < MWM_CMD_MAX; binding++) {
+ KeyCode code;
+
+ code = XKeysymToKeycode(mwm->display, binding->key);
+
+ XGrabKey(mwm->display, code, binding->mod,
+ mwm->root, True, GrabModeAsync, GrabModeAsync);
+ XGrabKey(mwm->display, code, binding->mod | LockMask,
+ mwm->root, True, GrabModeAsync, GrabModeAsync);
+ }
+
+ return(0);
+}
+
+int mwm_cmd(struct mwm *mwm, mwm_cmd_t cmd, void *data)
+{
+#ifdef MWM_DEBUG
+ static const char *cmd_names[] = {
+ "quit",
+ "spawn",
+ "show_workspace",
+ "move_to_workspace",
+ "(invalid)"
+ };
+
+ printf("%s(%p, %d [%s], %p)\n", __func__, (void*)mwm,
+ cmd, cmd_names[cmd < MWM_CMD_MAX ? cmd : MWM_CMD_MAX], data);
+#endif /* MWM_DEBUG */
+
+ if(!mwm || cmd < 0 || cmd >= MWM_CMD_MAX) {
+ return(-EINVAL);
+ }
+
+ if(!mwm->commands[cmd]) {
+ return(-ENOSYS);
+ }
+
+ mwm->commands[cmd](mwm, data);
+ return(0);
+}
+
+int mwm_get_atom(struct mwm *mwm, const char *name, long *dst)
+{
+ long atom;
+
+ if(!mwm || !name || !dst) {
+ return(-EINVAL);
}
- /* unregister monitor */
+ /* FIXME: Cache the result */
+ atom = XInternAtom(mwm->display, name, False);
+ *dst = atom;
return(0);
}