/* * DWN - Desktop Window Manager * retoor * Window decorations implementation */ #include "decorations.h" #include "client.h" #include "config.h" #include "util.h" #include #include #include #define BUTTON_SIZE 16 #define BUTTON_PADDING 4 #define RESIZE_EDGE 8 void decorations_init(void) { LOG_DEBUG("Decorations system initialized"); } void decorations_cleanup(void) { } void decorations_render(Client *client, bool focused) { if (client == NULL || client->frame == None) { return; } if (dwn == NULL || dwn->display == NULL || dwn->config == NULL) { return; } decorations_render_title_bar(client, focused); decorations_render_buttons(client, focused); decorations_render_border(client, focused); } void decorations_render_title_bar(Client *client, bool focused) { if (client == NULL || client->frame == None) { return; } if (dwn == NULL || dwn->display == NULL || dwn->gc == None || dwn->config == NULL) { return; } Display *dpy = dwn->display; const ColorScheme *colors = config_get_colors(); int title_height = config_get_title_height(); int border = client->border_width; unsigned long bg_color = focused ? colors->title_focused_bg : colors->title_unfocused_bg; unsigned long fg_color = focused ? colors->title_focused_fg : colors->title_unfocused_fg; XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, border, border, client->width, title_height); if (client->title[0] != '\0' && dwn->xft_font != NULL) { int text_y = border + (title_height + dwn->xft_font->ascent) / 2; int text_x = border + BUTTON_PADDING; int max_width = client->width - 3 * (BUTTON_SIZE + BUTTON_PADDING) - 2 * BUTTON_PADDING; if (max_width < BUTTON_SIZE) { return; } char display_title[256]; strncpy(display_title, client->title, sizeof(display_title) - 4); display_title[sizeof(display_title) - 4] = '\0'; XGlyphInfo extents; XftTextExtentsUtf8(dpy, dwn->xft_font, (const FcChar8 *)display_title, strlen(display_title), &extents); int text_width = extents.xOff; bool title_truncated = false; while (text_width > max_width && strlen(display_title) > 3) { size_t len = strlen(display_title); size_t cut = len - 1; while (cut > 0 && (display_title[cut] & 0xC0) == 0x80) { cut--; } if (cut > 0) cut--; while (cut > 0 && (display_title[cut] & 0xC0) == 0x80) { cut--; } display_title[cut] = '\0'; title_truncated = true; XftTextExtentsUtf8(dpy, dwn->xft_font, (const FcChar8 *)display_title, strlen(display_title), &extents); text_width = extents.xOff; } if (title_truncated) { strncat(display_title, "...", sizeof(display_title) - strlen(display_title) - 1); } XftDraw *xft_draw = XftDrawCreate(dpy, client->frame, DefaultVisual(dpy, dwn->screen), dwn->colormap); if (xft_draw != NULL) { XftColor xft_color; XRenderColor render_color; render_color.red = ((fg_color >> 16) & 0xFF) * 257; render_color.green = ((fg_color >> 8) & 0xFF) * 257; render_color.blue = (fg_color & 0xFF) * 257; render_color.alpha = 0xFFFF; XftColorAllocValue(dpy, DefaultVisual(dpy, dwn->screen), dwn->colormap, &render_color, &xft_color); XftDrawStringUtf8(xft_draw, &xft_color, dwn->xft_font, text_x, text_y, (const FcChar8 *)display_title, strlen(display_title)); XftColorFree(dpy, DefaultVisual(dpy, dwn->screen), dwn->colormap, &xft_color); XftDrawDestroy(xft_draw); } } } void decorations_render_buttons(Client *client, bool focused) { if (client == NULL || client->frame == None) { return; } if (dwn == NULL || dwn->display == NULL || dwn->gc == None || dwn->config == NULL) { return; } int min_buttons_width = 3 * (BUTTON_SIZE + BUTTON_PADDING) + BUTTON_PADDING; if (client->width < min_buttons_width) { return; } Display *dpy = dwn->display; const ColorScheme *colors = config_get_colors(); int title_height = config_get_title_height(); int border = client->border_width; int button_y = border + (title_height - BUTTON_SIZE) / 2; int close_x = border + client->width - BUTTON_SIZE - BUTTON_PADDING; int max_x = close_x - BUTTON_SIZE - BUTTON_PADDING; int min_x = max_x - BUTTON_SIZE - BUTTON_PADDING; if (close_x < border || max_x < border || min_x < border) { return; } unsigned long bg_color = focused ? colors->title_focused_bg : colors->title_unfocused_bg; XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, close_x, button_y, BUTTON_SIZE, BUTTON_SIZE); XSetForeground(dpy, dwn->gc, 0xcc4444); XDrawLine(dpy, client->frame, dwn->gc, close_x + 3, button_y + 3, close_x + BUTTON_SIZE - 4, button_y + BUTTON_SIZE - 4); XDrawLine(dpy, client->frame, dwn->gc, close_x + BUTTON_SIZE - 4, button_y + 3, close_x + 3, button_y + BUTTON_SIZE - 4); XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, max_x, button_y, BUTTON_SIZE, BUTTON_SIZE); XSetForeground(dpy, dwn->gc, 0x44cc44); XDrawRectangle(dpy, client->frame, dwn->gc, max_x + 3, button_y + 3, BUTTON_SIZE - 7, BUTTON_SIZE - 7); XSetForeground(dpy, dwn->gc, bg_color); XFillRectangle(dpy, client->frame, dwn->gc, min_x, button_y, BUTTON_SIZE, BUTTON_SIZE); XSetForeground(dpy, dwn->gc, 0xcccc44); XDrawLine(dpy, client->frame, dwn->gc, min_x + 3, button_y + BUTTON_SIZE - 5, min_x + BUTTON_SIZE - 4, button_y + BUTTON_SIZE - 5); } void decorations_render_border(Client *client, bool focused) { if (client == NULL || client->frame == None) { return; } if (dwn == NULL || dwn->display == NULL || dwn->config == NULL) { return; } const ColorScheme *colors = config_get_colors(); unsigned long border_color = focused ? colors->border_focused : colors->border_unfocused; XSetWindowBorder(dwn->display, client->frame, border_color); } ButtonType decorations_hit_test_button(Client *client, int x, int y) { if (client == NULL) { return BUTTON_COUNT; } int title_height = config_get_title_height(); int border = client->border_width; if (y < border || y > border + title_height) { return BUTTON_COUNT; } int button_y = border + (title_height - BUTTON_SIZE) / 2; int close_x = border + client->width - BUTTON_SIZE - BUTTON_PADDING; int max_x = close_x - BUTTON_SIZE - BUTTON_PADDING; int min_x = max_x - BUTTON_SIZE - BUTTON_PADDING; if (x >= close_x && x < close_x + BUTTON_SIZE && y >= button_y && y < button_y + BUTTON_SIZE) { return BUTTON_CLOSE; } if (x >= max_x && x < max_x + BUTTON_SIZE && y >= button_y && y < button_y + BUTTON_SIZE) { return BUTTON_MAXIMIZE; } if (x >= min_x && x < min_x + BUTTON_SIZE && y >= button_y && y < button_y + BUTTON_SIZE) { return BUTTON_MINIMIZE; } return BUTTON_COUNT; } bool decorations_hit_test_title_bar(Client *client, int x, int y) { if (client == NULL) { return false; } int title_height = config_get_title_height(); int border = client->border_width; if (y >= border && y < border + title_height) { ButtonType btn = decorations_hit_test_button(client, x, y); return btn == BUTTON_COUNT; } return false; } bool decorations_hit_test_resize_area(Client *client, int x, int y, int *direction) { if (client == NULL || direction == NULL) { return false; } int title_height = config_get_title_height(); int border = client->border_width; int frame_width = client->width + 2 * border; int frame_height = client->height + title_height + 2 * border; *direction = 0; bool left = (x < RESIZE_EDGE); bool right = (x > frame_width - RESIZE_EDGE); bool top = (y < RESIZE_EDGE); bool bottom = (y > frame_height - RESIZE_EDGE); if (!left && !right && !top && !bottom) { return false; } if (left) *direction |= 1; if (right) *direction |= 2; if (top) *direction |= 4; if (bottom) *direction |= 8; return true; } void decorations_button_press(Client *client, ButtonType button) { if (client == NULL) { return; } switch (button) { case BUTTON_CLOSE: LOG_DEBUG("Close button pressed for %s", client->title); client_close(client); break; case BUTTON_MAXIMIZE: LOG_DEBUG("Maximize button pressed for %s", client->title); client_toggle_maximize(client); break; case BUTTON_MINIMIZE: LOG_DEBUG("Minimize button pressed for %s", client->title); client_minimize(client); break; default: break; } } void decorations_draw_text(Window window, GC gc, int x, int y, const char *text, unsigned long color) { if (text == NULL || dwn == NULL || dwn->display == NULL) { return; } if (dwn->xft_font != NULL) { XftDraw *xft_draw = XftDrawCreate(dwn->display, window, DefaultVisual(dwn->display, dwn->screen), dwn->colormap); if (xft_draw != NULL) { XftColor xft_color; XRenderColor render_color; render_color.red = ((color >> 16) & 0xFF) * 257; render_color.green = ((color >> 8) & 0xFF) * 257; render_color.blue = (color & 0xFF) * 257; render_color.alpha = 0xFFFF; XftColorAllocValue(dwn->display, DefaultVisual(dwn->display, dwn->screen), dwn->colormap, &render_color, &xft_color); XftDrawStringUtf8(xft_draw, &xft_color, dwn->xft_font, x, y, (const FcChar8 *)text, strlen(text)); XftColorFree(dwn->display, DefaultVisual(dwn->display, dwn->screen), dwn->colormap, &xft_color); XftDrawDestroy(xft_draw); return; } } XSetForeground(dwn->display, gc, color); XDrawString(dwn->display, window, gc, x, y, text, strlen(text)); }