diff --git a/milkwm/milkwm.h b/milkwm/milkwm.h index f6070bc..5cd4eb8 100644 --- a/milkwm/milkwm.h +++ b/milkwm/milkwm.h @@ -34,6 +34,7 @@ void loop_wm(void); MwWidget wm_frame(int w, int h); void wm_destroy(MwWidget widget); void wm_set_name(MwWidget widget, const char* name); +void wm_focus(MwWidget widget, int focus); /* config.c */ extern config_t wm_config; diff --git a/milkwm/milkwmrc.in b/milkwm/milkwmrc.in index e1ca406..afee3d3 100644 --- a/milkwm/milkwmrc.in +++ b/milkwm/milkwmrc.in @@ -14,15 +14,12 @@ Window: { "Iconify" ]; }; - Background: { - # Just don't write anything if you want default background - #Inactive: "white"; - Active: "blue"; + Active: { + Background = "dark blue"; + Foreground = "white"; }; - Foreground: { - # Just don't write anything if you want default background - #Inactive: "black"; - Active: "white"; + Inactive: { + # Just don't write anything if you want default background/foreground }; # Left, Right, or Center diff --git a/milkwm/wm.c b/milkwm/wm.c index 65c19fa..097ba2d 100644 --- a/milkwm/wm.c +++ b/milkwm/wm.c @@ -1,14 +1,22 @@ #define _MILSKO #include "milkwm.h" +#include + static MwWidget root; typedef struct wmframe { MwWidget titlebar; MwWidget title; - int left_space; - int right_space; + MwWidget* left; + MwWidget* right; + + MwLLPixmap maximize; + MwLLPixmap iconify; + + int dragging; + MwPoint drag_point; } wmframe_t; void loop_wm(void) { @@ -42,6 +50,8 @@ static void resize(MwWidget handle, void* user, void* client) { wmframe_t* f = handle->opaque; int ww = MwGetInteger(handle, MwNwidth); int wh = MwGetInteger(handle, MwNheight); + int x; + int i; MwVaApply(f->titlebar, MwNx, MwDefaultBorderWidth(handle), @@ -50,10 +60,26 @@ static void resize(MwWidget handle, void* user, void* client) { MwNheight, TitleBarHeight + MwDefaultBorderWidth(handle) * 2, NULL); + x = MwDefaultBorderWidth(handle); + for(i = 0; i < arrlen(f->left); i++) { + MwVaApply(f->left[i], + MwNx, x, + NULL); + x += TitleBarHeight; + } + + x = MwGetInteger(f->titlebar, MwNwidth) - MwDefaultBorderWidth(handle); + for(i = 0; i < arrlen(f->right); i++) { + x -= TitleBarHeight; + MwVaApply(f->right[i], + MwNx, x, + NULL); + } + MwVaApply(f->title, - MwNx, MwDefaultBorderWidth(f->titlebar) + f->left_space + 8, + MwNx, MwDefaultBorderWidth(f->titlebar) + arrlen(f->left) * TitleBarHeight, MwNy, MwDefaultBorderWidth(f->titlebar), - MwNwidth, MwGetInteger(f->titlebar, MwNwidth) - MwDefaultBorderWidth(f->titlebar) * 2 - f->left_space - f->right_space - 16, + MwNwidth, MwGetInteger(f->titlebar, MwNwidth) - MwDefaultBorderWidth(f->titlebar) * 2 - arrlen(f->left) * TitleBarHeight - arrlen(f->right) * TitleBarHeight, MwNheight, MwGetInteger(f->titlebar, MwNheight) - MwDefaultBorderWidth(f->titlebar) * 2, NULL); } @@ -81,10 +107,88 @@ static void apply_config(MwWidget wnd) { } } +static void apply_button(MwWidget widget, const char* str) { + MwWidget w = widget; + wmframe_t* f; + MwLLPixmap px = NULL; + + while(w != NULL && strcmp(MwGetName(w), "frame") != 0) w = MwGetParent(w); + + f = w->opaque; + + if(strcmp(str, "Maximize") == 0) { + px = f->maximize; + } else if(strcmp(str, "Iconify") == 0) { + px = f->iconify; + } + + if(px != NULL) { + int p = (MwGetInteger(widget, MwNwidth) - MwDefaultBorderWidth(widget) * 2 - px->common.width) / 2; + MwVaApply(widget, + MwNpixmap, px, + MwNpadding, p, + NULL); + } +} +static void drag_down(MwWidget handle, void* user, void* client) { + MwWidget w = handle; + wmframe_t* f; + int x = 0; + int y = 0; + MwPoint c; + + while(w != NULL && strcmp(MwGetName(w), "frame") != 0) w = MwGetParent(w); + x += MwGetInteger(w, MwNx); + y += MwGetInteger(w, MwNy); + + f = w->opaque; + + MwGetCursorCoord(w, &c); + + f->dragging = 1; + f->drag_point.x = c.x - x; + f->drag_point.y = c.y - y; +} + +static void drag_up(MwWidget handle, void* user, void* client) { + MwWidget w = handle; + wmframe_t* f; + + while(w != NULL && strcmp(MwGetName(w), "frame") != 0) w = MwGetParent(w); + + f = w->opaque; + + f->dragging = 0; +} + +static void drag(MwWidget handle, void* user, void* client) { + MwWidget w = handle; + wmframe_t* f; + + while(w != NULL && strcmp(MwGetName(w), "frame") != 0) w = MwGetParent(w); + + f = w->opaque; + + if(f->dragging) { + MwPoint c; + + MwGetCursorCoord(w, &c); + + MwVaApply(w, + MwNx, c.x - f->drag_point.x, + MwNy, c.y - f->drag_point.y, + NULL); + } +} + MwWidget wm_frame(int w, int h) { - MwWidget wnd, titlebar; - int i; - wmframe_t* f = malloc(sizeof(*f)); + MwWidget wnd, titlebar; + int i; + config_setting_t* s; + const char* str; + wmframe_t* f = malloc(sizeof(*f)); + unsigned char* icon; + int y, x; pthread_mutex_lock(&xmutex); @@ -94,17 +198,66 @@ MwWidget wm_frame(int w, int h) { NULL); wnd->opaque = f; - f->left_space = TitleBarHeight; - f->right_space = 0; + MwAddUserHandler(wnd, MwNresizeHandler, resize, NULL); + + f->dragging = 0; + + icon = malloc(6 * 6 * 4); + + memset(icon, 0, 6 * 6 * 4); + for(y = 0; y < 6; y++) { + for(x = 0; x < 6; x++) { + if((y == 0 || y == 5 || x == 0 || x == 5) && !(2 <= y && y <= 3) && !(2 <= x && x <= 3)) icon[(y * 6 + x) * 4 + 3] = 255; + } + } + f->iconify = MwLoadRaw(wnd, icon, 6, 6); + + memset(icon, 0, 6 * 6 * 4); + for(y = 0; y < 6; y++) { + for(x = 0; x < 6; x++) { + if(y == 0 || y == 5 || x == 0 || x == 5) icon[(y * 6 + x) * 4 + 3] = 255; + } + } + f->maximize = MwLoadRaw(wnd, icon, 6, 6); + free(icon); + + f->left = NULL; + f->right = NULL; f->titlebar = MwVaCreateWidget(MwFrameClass, "titlebar", wnd, 0, 0, 0, 0, MwNhasBorder, 1, MwNinverted, 1, NULL); + MwAddUserHandler(f->titlebar, MwNmouseUpHandler, drag_up, NULL); + MwAddUserHandler(f->titlebar, MwNmouseMoveHandler, drag, NULL); + MwAddUserHandler(f->titlebar, MwNmouseDownHandler, drag_down, NULL); f->title = MwCreateWidget(MwLabelClass, "title", f->titlebar, 0, 0, 0, 0); + MwAddUserHandler(f->title, MwNmouseUpHandler, drag_up, NULL); + MwAddUserHandler(f->title, MwNmouseMoveHandler, drag, NULL); + MwAddUserHandler(f->title, MwNmouseDownHandler, drag_down, NULL); - MwCreateWidget(MwButtonClass, "button", f->titlebar, MwDefaultBorderWidth(wnd), MwDefaultBorderWidth(wnd), TitleBarHeight, TitleBarHeight); + s = config_lookup(&wm_config, "Window.TitleBar.Buttons.Left"); + for(i = 0; (str = config_setting_get_string_elem(s, i)) != NULL; i++) { + MwWidget w = MwVaCreateWidget(MwButtonClass, "button", f->titlebar, 0, MwDefaultBorderWidth(wnd), TitleBarHeight, TitleBarHeight, + MwNborderWidth, 1, + NULL); + + apply_button(w, str); + + arrput(f->left, w); + } + + s = config_lookup(&wm_config, "Window.TitleBar.Buttons.Right"); + for(i = 0; (str = config_setting_get_string_elem(s, i)) != NULL; i++) { + MwWidget w = MwVaCreateWidget(MwButtonClass, "button", f->titlebar, 0, MwDefaultBorderWidth(wnd), TitleBarHeight, TitleBarHeight, + MwNborderWidth, 1, + NULL); + + apply_button(w, str); + + arrput(f->right, w); + } apply_config(wnd); @@ -116,7 +269,14 @@ MwWidget wm_frame(int w, int h) { } void wm_destroy(MwWidget widget) { + wmframe_t* f = widget->opaque; + pthread_mutex_lock(&xmutex); + arrfree(f->left); + arrfree(f->right); + + MwLLDestroyPixmap(f->maximize); + MwLLDestroyPixmap(f->iconify); free(widget->opaque); MwDestroyWidget(widget); pthread_mutex_unlock(&xmutex); @@ -124,10 +284,35 @@ void wm_destroy(MwWidget widget) { void wm_set_name(MwWidget widget, const char* name) { wmframe_t* f = widget->opaque; + char* s = MDEStringConcatenate(" ", name); pthread_mutex_lock(&xmutex); MwVaApply(f->title, - MwNtext, name, + MwNtext, s, NULL); pthread_mutex_unlock(&xmutex); + + free(s); +} + +void wm_focus(MwWidget widget, int focus) { + wmframe_t* f = widget->opaque; + const char* bg = NULL; + const char* fg = NULL; + + pthread_mutex_lock(&xmutex); + if(focus) { + config_lookup_string(&wm_config, "Window.TitleBar.Active.Background", &bg); + config_lookup_string(&wm_config, "Window.TitleBar.Active.Foreground", &fg); + } else { + config_lookup_string(&wm_config, "Window.TitleBar.Inactive.Background", &bg); + config_lookup_string(&wm_config, "Window.TitleBar.Inactive.Foreground", &fg); + } + + MwVaApply(f->title, + MwNbackground, bg, + MwNforeground, fg, + NULL); + + pthread_mutex_unlock(&xmutex); } diff --git a/milkwm/xserver.c b/milkwm/xserver.c index 108588a..e164c8c 100644 --- a/milkwm/xserver.c +++ b/milkwm/xserver.c @@ -8,6 +8,12 @@ Display* xdisplay; pthread_t xthread; pthread_mutex_t xmutex; +typedef struct window { + MwWidget frame; + Window client; +} window_t; +static window_t* windows = NULL; + static void* old_handler; static int error_happened = 0; static int ignore_error(Display* disp, XErrorEvent* ev) { @@ -32,13 +38,23 @@ static void* x11_thread_routine(void* arg) { } static Window focus = None, nofocus; -static void set_focus(Window w) { - if(w == None) { - XSetInputFocus(xdisplay, nofocus, RevertToParent, CurrentTime); - } else { - XSetInputFocus(xdisplay, w, RevertToParent, CurrentTime); - } - focus = w; + +static void set_focus(Window w) { + int i; + for(i = 0; i < arrlen(windows); i++) { + if(windows[i].client == w) { + wm_focus(windows[i].frame, 1); + } else if(focus == windows[i].client) { + wm_focus(windows[i].frame, 0); + } + } + + if(w == None) { + XSetInputFocus(xdisplay, nofocus, RevertToParent, CurrentTime); + } else { + XSetInputFocus(xdisplay, w, RevertToParent, CurrentTime); + } + focus = w; } int init_x(void) { @@ -71,12 +87,6 @@ int init_x(void) { return 0; } -typedef struct window { - MwWidget frame; - Window client; -} window_t; -static window_t* windows = NULL; - static void save(Window w) { XSelectInput(xdisplay, w, StructureNotifyMask | FocusChangeMask | PropertyChangeMask); XSetWindowBorderWidth(xdisplay, w, 0); @@ -100,13 +110,13 @@ static void set_name(Window wnd) { Atom type; int format; unsigned long nitem, after; - unsigned char* buf; + unsigned char* buf = NULL; Atom atom = XInternAtom(xdisplay, "WM_NAME", False); int i; for(i = 0; i < arrlen(windows); i++) { if(windows[i].client == wnd) { - if(XGetWindowProperty(xdisplay, wnd, atom, 0, 1024, False, XA_STRING, &type, &format, &nitem, &after, &buf) == Success) { + if(XGetWindowProperty(xdisplay, wnd, atom, 0, 1024, False, XA_STRING, &type, &format, &nitem, &after, &buf) == Success && buf != NULL) { wm_set_name(windows[i].frame, buf); XFree(buf); } @@ -169,8 +179,13 @@ void loop_x(void) { int i; Window w = ev.xconfigurerequest.window; for(i = 0; i < arrlen(windows); i++) { - if(windows[i].client == w) { - w = windows[i].frame->lowlevel->x11.window; + if(windows[i].frame->lowlevel->x11.window == w) { + xwc.x = ev.xconfigurerequest.x; + xwc.y = ev.xconfigurerequest.y; + xwc.width = ev.xconfigurerequest.width - MwDefaultBorderWidth(windows[i].frame) * 2; + xwc.height = ev.xconfigurerequest.height - MwDefaultBorderWidth(windows[i].frame) * 4 - TitleBarHeight; + + XConfigureWindow(xdisplay, windows[i].client, CWX | CWY | CWWidth | CWHeight, &xwc); break; } } @@ -187,6 +202,7 @@ void loop_x(void) { if(windows[i].client == ev.xconfigure.window) { XWindowChanges xwc; XWindowAttributes xwa; + xwc.x = ev.xconfigure.x; xwc.y = ev.xconfigure.y; xwc.width = MwDefaultBorderWidth(windows[i].frame) * 2 + ev.xconfigure.width; @@ -203,6 +219,7 @@ void loop_x(void) { } else if(xwa.width != xwc.width || xwa.height != xwc.height) { XConfigureWindow(xdisplay, windows[i].frame->lowlevel->x11.window, CWWidth | CWHeight, &xwc); } + break; } } @@ -234,14 +251,11 @@ void loop_x(void) { XMoveWindow(xdisplay, windows[i].frame->lowlevel->x11.window, xwa.x, xwa.y); XMapWindow(xdisplay, ev.xmaprequest.window); - pthread_mutex_lock(&xmutex); if(!XReparentWindow(xdisplay, windows[i].client, windows[i].frame->lowlevel->x11.window, MwDefaultBorderWidth(windows[i].frame), MwDefaultBorderWidth(windows[i].frame) * 3 + TitleBarHeight)) { - pthread_mutex_unlock(&xmutex); wm_destroy(windows[i].frame); arrdel(windows, i); continue; } - pthread_mutex_unlock(&xmutex); if(!XMapWindow(xdisplay, windows[i].client)) { wm_destroy(windows[i].frame); arrdel(windows, i); @@ -285,9 +299,7 @@ void loop_x(void) { XWindowAttributes xwa; if(XGetWindowAttributes(xdisplay, windows[i].frame->lowlevel->x11.window, &xwa)) { - pthread_mutex_lock(&xmutex); XReparentWindow(xdisplay, windows[i].client, DefaultRootWindow(xdisplay), xwa.x, xwa.y); - pthread_mutex_unlock(&xmutex); } wm_destroy(windows[i].frame);