diff --git a/src/aswm/aswm.c b/src/aswm/aswm.c index 129d50d..5a27318 100644 --- a/src/aswm/aswm.c +++ b/src/aswm/aswm.c @@ -17,8 +17,8 @@ Aswm _aswm; void aswm_open() { _aswm.display = XOpenDisplay(NULL); - _aswm.workspaces = malloc(0); - _aswm.workspace_count = 0; + _aswm.desktops = malloc(0); + _aswm.desktops_count = 0; XSetErrorHandler(aswm_error_handler); @@ -53,22 +53,34 @@ void aswm_open() { } void aswm_create_workspace() { - _aswm.workspaces = realloc(_aswm.workspaces, (_aswm.workspace_count+1) * sizeof(Window)); + _aswm.desktops = realloc(_aswm.desktops, (_aswm.desktops_count+1) * sizeof(Desktop)); XWindowAttributes root_attributes; XGetWindowAttributes(_aswm.display, _aswm.root_window, &root_attributes); - _aswm.workspaces[_aswm.workspace_count] = XCreateSimpleWindow( + _aswm.desktops[_aswm.desktops_count].root_window = XCreateSimpleWindow( _aswm.display, _aswm.root_window, 0, 0, root_attributes.width, root_attributes.height, 0, 0, 0); + _aswm.desktops[_aswm.desktops_count].tile_root = XCreateSimpleWindow( + _aswm.display, _aswm.desktops[_aswm.desktops_count].root_window, + 0, 0, + root_attributes.width, root_attributes.height, + 0, 0, 0); + _aswm.desktops[_aswm.desktops_count].stack_root = XCreateSimpleWindow( + _aswm.display, _aswm.desktops[_aswm.desktops_count].root_window, + 0, 0, + root_attributes.width, root_attributes.height, + 0, 0, 0); - _aswm.current_workspace = _aswm.workspaces[_aswm.workspace_count]; - _aswm.workspace_count++; - XSelectInput(_aswm.display, _aswm.current_workspace, SubstructureNotifyMask); - XMapWindow(_aswm.display, _aswm.current_workspace); - printf("Create workspace %lu\n", _aswm.current_workspace); + _aswm.current_desktop = _aswm.desktops[_aswm.desktops_count]; + _aswm.desktops_count++; + XSelectInput(_aswm.display, _aswm.current_desktop.tile_root, SubstructureNotifyMask); + XSelectInput(_aswm.display, _aswm.current_desktop.stack_root, SubstructureNotifyMask); + XMapWindow(_aswm.display, _aswm.current_desktop.root_window); + XMapWindow(_aswm.display, _aswm.current_desktop.tile_root); + printf("Create workspace %lu\n", _aswm.current_desktop.root_window); } void aswm_event_loop() { @@ -139,7 +151,7 @@ void aswm_event_loop() { } void aswm_close() { - free(_aswm.workspaces); + free(_aswm.desktops); XCloseDisplay(_aswm.display); } diff --git a/src/aswm/aswm.h b/src/aswm/aswm.h index 98e97d3..ce54b78 100644 --- a/src/aswm/aswm.h +++ b/src/aswm/aswm.h @@ -3,12 +3,18 @@ #include "mapper/mapper.h" +typedef struct { + Window root_window; + Window tile_root; + Window stack_root; +} Desktop; + typedef struct { Display *display; Window root_window; - Window* workspaces; - unsigned int workspace_count; - Window current_workspace; + Desktop* desktops; + unsigned int desktops_count; + Desktop current_desktop; } Aswm; void aswm_open(); diff --git a/src/aswm/event_handlers/create.c b/src/aswm/event_handlers/create.c index a3e16df..ee0d56c 100644 --- a/src/aswm/event_handlers/create.c +++ b/src/aswm/event_handlers/create.c @@ -35,6 +35,6 @@ void OnCreateNotify(Aswm* aswm, const XCreateWindowEvent* e) { XFree(class.res_name); } } - if(aswm->workspace_count == 0) + if(aswm->desktops_count == 0) aswm_create_workspace(); } diff --git a/src/aswm/event_handlers/map.c b/src/aswm/event_handlers/map.c index ba74771..60d940a 100644 --- a/src/aswm/event_handlers/map.c +++ b/src/aswm/event_handlers/map.c @@ -1,27 +1,30 @@ #include "map.h" #include #include "aswm/log/log.h" +#include "aswm/mapper/mapper.h" #include "X11/Xutil.h" void OnMapRequest(Aswm* aswm, const XMapRequestEvent* e) { printf("XMapRequestEvent %lu\n", e->window); - /* XSizeHints* size = XAllocSizeHints(); */ - /* long supplied; */ - /* if(XGetWMNormalHints(aswm->display, e->window, size, &supplied)) { */ - /* printf("\tWMNormalHints:\n"); */ - /* printf("\t\tMin w:%i\n", size->min_width); */ - /* printf("\t\tMin h:%i\n", size->min_height); */ - /* printf("\t\tMax w:%i\n", size->max_width); */ - /* printf("\t\tMax h:%i\n", size->max_height); */ - /* printf("\t\tBase w:%i\n", size->base_width); */ - /* printf("\t\tBase h:%i\n", size->base_height); */ - /* } */ - /* XFree(size); */ + XSizeHints* size = XAllocSizeHints(); + long supplied; + bool stacked = false; + if(XGetWMNormalHints(aswm->display, e->window, size, &supplied)) { + if((supplied & (PMinSize | PMaxSize)) && !aswm_is_resizeable(size)) { + printf("\tWMNormalHints:\n"); + printf("\t\tMin w:%i\n", size->min_width); + printf("\t\tMin h:%i\n", size->min_height); + printf("\t\tMax w:%i\n", size->max_width); + printf("\t\tMax h:%i\n", size->max_height); + aswm_stack_window(e->display, aswm->current_desktop.stack_root, e->window, size->max_width, size->max_height); + stacked = true; + } + } + XFree(size); - // Assumes map requests are always received on root and adressed to the - // current workspace - aswm_map_new_window(e->display, aswm->current_workspace, e->window); + if(!stacked) + aswm_tile_window(e->display, aswm->current_desktop.tile_root, e->window); printf("Map tree:\n"); log_tree(aswm, DefaultRootWindow(aswm->display), 1); diff --git a/src/aswm/event_handlers/property.c b/src/aswm/event_handlers/property.c index fc628b2..be4c4db 100644 --- a/src/aswm/event_handlers/property.c +++ b/src/aswm/event_handlers/property.c @@ -3,12 +3,14 @@ #include #include #include +#include "aswm/mapper/mapper.h" void OnPropertyNotify(Aswm* aswm, XPropertyEvent* e) { - printf("Unhandled property change for window %lu:\n", e->window); + printf("Property change for window %lu:\n", e->window); char* property_name = XGetAtomName(e->display, e->atom); printf("\t%s\n", property_name); if(strcmp(property_name, "_NET_WM_NAME") == 0) { + printf("\t-> Unhandled\n"); XTextProperty wm_name_property; char** wm_name; int wm_name_count; @@ -18,6 +20,7 @@ void OnPropertyNotify(Aswm* aswm, XPropertyEvent* e) { printf("\t_NET_WM_NAME: %s\n", wm_name[i]); XFreeStringList(wm_name); } else if(strcmp(property_name, "WM_NAME") == 0) { + printf("\t-> Unhandled\n"); XTextProperty text_property; if(XGetWMName(e->display, e->window, &text_property)) { printf("\tWM_NAME: %s\n", text_property.value); @@ -33,9 +36,13 @@ void OnPropertyNotify(Aswm* aswm, XPropertyEvent* e) { printf("\t\tMax h:%i\n", size->max_height); printf("\t\tBase w:%i\n", size->base_width); printf("\t\tBase h:%i\n", size->base_height); + aswm_handle_normal_hints( + aswm->display, aswm->current_desktop.stack_root, + e->window, size, supplied); } XFree(size); } else if(strcmp(property_name, "WM_HINTS") == 0) { + printf("\t-> Unhandled\n"); XWMHints* hints = XGetWMHints(aswm->display, e->window); printf("\t\tflags Input: %li\n", hints->flags & InputHint); printf("\t\tflags Input: %li\n", hints->flags & StateHint); @@ -49,6 +56,7 @@ void OnPropertyNotify(Aswm* aswm, XPropertyEvent* e) { printf("\t\tinitial_state: %i\n", hints->initial_state); XFree(hints); } else if(strcmp(property_name, "WM_PROTOCOLS") == 0) { + printf("\t-> Unhandled\n"); Atom* protocols; int count; XGetWMProtocols(aswm->display, e->window, &protocols, &count); @@ -63,6 +71,8 @@ void OnPropertyNotify(Aswm* aswm, XPropertyEvent* e) { printf("\n"); XFree(protocols); + } else { + printf("\t-> Unhandled\n"); } XFree(property_name); } diff --git a/src/aswm/event_handlers/unmap.c b/src/aswm/event_handlers/unmap.c index 6af785b..c70bad2 100644 --- a/src/aswm/event_handlers/unmap.c +++ b/src/aswm/event_handlers/unmap.c @@ -16,7 +16,13 @@ void OnUnmapNotify(Aswm* aswm, const XUnmapEvent* e) { // Unmap should have no effect on not map windows. Currently, all mapped // windows are children of aswm->root_window. if(e->event != XDefaultRootWindow(e->display)) { - aswm_unmap_window(e->display, e->event, e->window); + if(e->event == aswm->current_desktop.tile_root) { + printf("aswm untile\n"); + aswm_untile_window(e->display, e->event, e->window); + } else if(e->event == aswm->current_desktop.stack_root) { + printf("aswm unstack\n"); + aswm_unstack_window(e->display, e->event); + } printf("Unmap tree:\n"); log_tree(aswm, DefaultRootWindow(aswm->display), 1); } diff --git a/src/aswm/log/error.h b/src/aswm/log/error.h index d119390..fd00c24 100644 --- a/src/aswm/log/error.h +++ b/src/aswm/log/error.h @@ -8,7 +8,7 @@ char message_buffer[1024]; int aswm_error_handler( Display *disp, XErrorEvent *xe ) { XGetErrorText(xe->display, xe->error_code, message_buffer, 1024); - printf("%s", message_buffer); + printf("%s\n", message_buffer); return 0; } diff --git a/src/aswm/log/log.c b/src/aswm/log/log.c index e267eca..0cdebac 100644 --- a/src/aswm/log/log.c +++ b/src/aswm/log/log.c @@ -11,8 +11,12 @@ void log_tree(Aswm* aswm, Window window, int indent) { printf("(X root) "); } else if (window == aswm->root_window) { printf("(ASWM root) "); - } else if (window == aswm->current_workspace) { + } else if (window == aswm->current_desktop.root_window) { printf("(current workspace) "); + } else if (window == aswm->current_desktop.tile_root) { + printf("(tile root) "); + } else if (window == aswm->current_desktop.stack_root) { + printf("(stack root) "); } printf("at (%i, %i): %ix%i\n", window_attributes.x, window_attributes.y, window_attributes.width, window_attributes.height); diff --git a/src/aswm/mapper/mapper.c b/src/aswm/mapper/mapper.c index cbf7e90..70f5049 100644 --- a/src/aswm/mapper/mapper.c +++ b/src/aswm/mapper/mapper.c @@ -2,13 +2,19 @@ #include #include "aswm/mapper/mapper.h" #include "aswm/log/log.h" +#include + +bool aswm_is_resizeable(XSizeHints* size) { + return size->min_width != size->max_width + || size->min_height != size->max_height; +} /** * Returns the free tile built in the split process. */ void h_split(Display* display, Window current_node, Window window_to_map); -void aswm_map_new_window(Display* display, Window current_node, Window window_to_map) { +void aswm_tile_window(Display* display, Window current_node, Window window_to_map) { h_split(display, current_node, window_to_map); } @@ -82,7 +88,7 @@ void resize_children_to_parent(Display* display, Window parent) { XFree(children); } -void aswm_unmap_window(Display* display, Window parent, Window window) { +void aswm_untile_window(Display* display, Window parent, Window window) { // +-------------------------------+ // | root | // |+-------------+ +-------------+| @@ -122,3 +128,140 @@ void aswm_unmap_window(Display* display, Window parent, Window window) { resize_children_to_parent(display, parent); } + +/** + * Currently, resize to the max width and max height of any children, but should + * be reconsidered as it can create black square artifacts if the max width and + * height are not for the same window. + */ +void resize_stack(Display* display, Window stack) { + Window root; // unused + Window parent; + Window* children; + unsigned int children_count; + XQueryTree(display, stack, &root, &parent, &children, &children_count); + + unsigned int width = 0; + unsigned int height = 0; + for(auto i = 0; i < children_count; i++) { + XWindowAttributes child_attributes; + XGetWindowAttributes(display, children[i], &child_attributes); + if(child_attributes.map_state != IsUnmapped) { + if(child_attributes.width > width) + width = child_attributes.width; + if(child_attributes.height > height) + height = child_attributes.height; + } + } + + // The current method is always called when there is at least a child, so + // children is never NULL + XFree(children); + + XWindowAttributes parent_attributes; + XGetWindowAttributes(display, parent, &parent_attributes); + XMoveResizeWindow(display, stack, + (parent_attributes.width - width) / 2, + (parent_attributes.height - height) / 2, + width, height); +} + +void aswm_stack_window(Display* display, Window stack, Window window_to_map, int width, int height) { + XWindowAttributes stack_attributes; + XGetWindowAttributes(display, stack, &stack_attributes); + + XSetWindowAttributes stacked_attributes; + stack_attributes.width = width; + stack_attributes.height = height; + stack_attributes.win_gravity = CenterGravity; + XChangeWindowAttributes(display, window_to_map, + CWWidth | CWHeight | CWWinGravity, + &stacked_attributes); + XReparentWindow(display, window_to_map, stack, + (stack_attributes.width - width) / 2, + (stack_attributes.height - height) / 2 + ); + // Map before resize so window_to_map can be taken into account in resizing + // of stack + XMapWindow(display, window_to_map); + + resize_stack(display, stack); + + if(stack_attributes.map_state == IsUnmapped) { + XMapWindow(display, stack); + } +} + +void aswm_unstack_window(Display* display, Window stack) { + Window root; // unused + Window parent; + Window* children; + unsigned int children_count; + XQueryTree(display, stack, &root, &parent, &children, &children_count); + + bool visible_child = false; + int i = 0; + while(!visible_child && i < children_count) { + XWindowAttributes child_attributes; + XGetWindowAttributes(display, children[i], &child_attributes); + visible_child = (child_attributes.map_state == IsViewable); + i++; + } + XFree(children); + + if(visible_child) { + printf("Resize stack %lu\n", stack); + resize_stack(display, stack); + } else { + printf("Nothing in stack, unmap %lu\n", stack); + XWindowAttributes parent_size; + XGetWindowAttributes(display, parent, &parent_size); + + XMoveResizeWindow(display, stack, 0, 0, parent_size.width, parent_size.height); + XUnmapWindow(display, stack); + } +} + +bool is_in_stack(Display* display, Window stack_root, Window window) { + Window _root; // unused + Window parent; + Window* _children; // unused + unsigned int _children_count; // unused + + XQueryTree(display, window, &_root, &parent, &_children, &_children_count); + return parent == stack_root; +} + +void aswm_handle_normal_hints(Display* display, Window stack_root, Window window, XSizeHints* size, long supplied) { + printf("Handle normal hints for %lu\n", window); + XWindowAttributes win_attributes; + XGetWindowAttributes(display, window, &win_attributes); + + if(win_attributes.map_state != IsUnmapped) { + // TODO: handle multiple desktops + if((supplied & (PMinSize | PMaxSize))) { + if(is_in_stack(display, stack_root, window)) { + if(aswm_is_resizeable(size)) { + // Remap to tile root + XUnmapWindow(display, window); + XMapWindow(display, window); + } else { + XResizeWindow(display, window, size->max_width, size->max_height); + resize_stack(display, stack_root); + } + } else { + if(!aswm_is_resizeable(size)) { + // Resize window to its fixed size before stacking it + XResizeWindow(display, window, size->max_width, size->max_height); + + // XMapWindow does not trigger a MapRequest for the current + // client, so we need to call aswm_stack_window directly. + // aswm_stack_window automatically triggers the appropriate + // unmap from tile when reparenting from tile to stack + aswm_stack_window(display, stack_root, window, size->max_width, size->max_height); + } + } + } + } +} + diff --git a/src/aswm/mapper/mapper.h b/src/aswm/mapper/mapper.h index ea82ecd..2e6af65 100644 --- a/src/aswm/mapper/mapper.h +++ b/src/aswm/mapper/mapper.h @@ -2,10 +2,17 @@ #define ASWM_MAPPER #include +#include Window create_aswm_root_tile(Display* display, Window root_window); -void aswm_map_new_window(Display* display, Window current_node, Window window_to_map); -void aswm_unmap_window(Display* display, Window parent, Window window); +void aswm_tile_window(Display* display, Window current_node, Window window_to_map); +void aswm_untile_window(Display* display, Window parent, Window window); + +void aswm_stack_window(Display* display, Window stack, Window window_to_map, int width, int height); +void aswm_unstack_window(Display* display, Window stack); +void aswm_handle_normal_hints(Display* display, Window stack_root, Window window, XSizeHints* size, long supplied); + +bool aswm_is_resizeable(XSizeHints* size); #endif