2025-12-28 03:14:31 +01:00
|
|
|
/*
|
|
|
|
|
* DWN - Desktop Window Manager
|
2025-12-28 04:30:10 +01:00
|
|
|
* retoor <retoor@molodetz.nl>
|
2025-12-28 03:14:31 +01:00
|
|
|
* Client (window) management implementation
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "client.h"
|
|
|
|
|
#include "atoms.h"
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "workspace.h"
|
|
|
|
|
#include "decorations.h"
|
|
|
|
|
#include "notifications.h"
|
2025-12-28 10:23:03 +01:00
|
|
|
#include "layout.h"
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <X11/Xresource.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Client *client_create(Window window)
|
|
|
|
|
{
|
|
|
|
|
if (dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
LOG_ERROR("client_create: dwn or display is NULL");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (window == None) {
|
|
|
|
|
LOG_ERROR("client_create: invalid window (None)");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *client = dwn_calloc(1, sizeof(Client));
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
LOG_ERROR("client_create: failed to allocate client");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->window = window;
|
|
|
|
|
client->frame = None;
|
|
|
|
|
client->border_width = config_get_border_width();
|
|
|
|
|
client->flags = CLIENT_NORMAL;
|
|
|
|
|
client->workspace = dwn->current_workspace;
|
|
|
|
|
client->next = NULL;
|
|
|
|
|
client->prev = NULL;
|
2025-12-28 10:23:03 +01:00
|
|
|
client->mru_next = NULL;
|
|
|
|
|
client->mru_prev = NULL;
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
int orig_width = 640, orig_height = 480;
|
|
|
|
|
if (XGetWindowAttributes(dwn->display, window, &wa)) {
|
|
|
|
|
orig_width = wa.width;
|
|
|
|
|
orig_height = wa.height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int work_x = 0;
|
|
|
|
|
int work_y = 0;
|
|
|
|
|
int work_width = dwn->screen_width;
|
|
|
|
|
int work_height = dwn->screen_height;
|
|
|
|
|
|
|
|
|
|
if (dwn->config != NULL) {
|
|
|
|
|
if (dwn->config->top_panel_enabled) {
|
|
|
|
|
work_y += config_get_panel_height();
|
|
|
|
|
work_height -= config_get_panel_height();
|
|
|
|
|
}
|
|
|
|
|
if (dwn->config->bottom_panel_enabled) {
|
|
|
|
|
work_height -= config_get_panel_height();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 09:26:53 +01:00
|
|
|
client->width = orig_width;
|
|
|
|
|
client->height = orig_height;
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
if (client->width > work_width - 20) client->width = work_width - 20;
|
|
|
|
|
if (client->height > work_height - 20) client->height = work_height - 20;
|
|
|
|
|
|
|
|
|
|
client->x = work_x + (work_width - client->width) / 2;
|
|
|
|
|
client->y = work_y + (work_height - client->height) / 2;
|
|
|
|
|
|
|
|
|
|
client->old_x = client->x;
|
|
|
|
|
client->old_y = client->y;
|
|
|
|
|
client->old_width = client->width;
|
|
|
|
|
client->old_height = client->height;
|
|
|
|
|
|
|
|
|
|
client_update_title(client);
|
|
|
|
|
client_update_class(client);
|
|
|
|
|
|
|
|
|
|
return client;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_destroy(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->frame != None) {
|
|
|
|
|
client_destroy_frame(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dwn_free(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void client_sync_log(const char *msg)
|
|
|
|
|
{
|
2025-12-28 05:01:46 +01:00
|
|
|
(void)msg;
|
2025-12-28 03:14:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Client *client_manage(Window window)
|
|
|
|
|
{
|
|
|
|
|
client_sync_log("client_manage: START");
|
|
|
|
|
|
|
|
|
|
if (dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
client_sync_log("client_manage: dwn NULL");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *existing = client_find_by_window(window);
|
|
|
|
|
if (existing != NULL) {
|
|
|
|
|
client_sync_log("client_manage: already managed");
|
|
|
|
|
return existing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client_is_dock(window) || client_is_desktop(window)) {
|
|
|
|
|
LOG_DEBUG("Skipping dock/desktop window: %lu", window);
|
|
|
|
|
client_sync_log("client_manage: skip dock/desktop");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("Managing window: %lu", window);
|
|
|
|
|
client_sync_log("client_manage: creating client");
|
|
|
|
|
|
|
|
|
|
Client *client = client_create(window);
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
LOG_ERROR("client_manage: failed to create client for window %lu", window);
|
|
|
|
|
client_sync_log("client_manage: create FAILED");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: checking dialog");
|
|
|
|
|
|
|
|
|
|
if (client_is_dialog(window)) {
|
|
|
|
|
client->flags |= CLIENT_FLOATING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: creating frame");
|
|
|
|
|
|
|
|
|
|
client_create_frame(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: verifying window");
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, window, &wa)) {
|
|
|
|
|
LOG_WARN("client_manage: window %lu disappeared before reparenting", window);
|
|
|
|
|
client_sync_log("client_manage: window gone before reparent");
|
|
|
|
|
client_destroy(client);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: reparenting");
|
|
|
|
|
client_reparent_to_frame(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: verifying after reparent");
|
2025-12-28 05:01:46 +01:00
|
|
|
XSync(dwn->display, False);
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
if (client->frame == None) {
|
|
|
|
|
client_sync_log("client_manage: frame is None after reparent");
|
|
|
|
|
LOG_WARN("client_manage: frame creation failed for window %lu", window);
|
|
|
|
|
client_destroy(client);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa_frame;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->frame, &wa_frame)) {
|
|
|
|
|
client_sync_log("client_manage: frame gone after reparent");
|
|
|
|
|
LOG_WARN("client_manage: frame %lu no longer exists", client->frame);
|
|
|
|
|
client_destroy(client);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, window, &wa)) {
|
|
|
|
|
client_sync_log("client_manage: window gone after reparent");
|
|
|
|
|
LOG_WARN("client_manage: window %lu disappeared after reparent", window);
|
|
|
|
|
client_destroy_frame(client);
|
|
|
|
|
client_destroy(client);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: configuring");
|
|
|
|
|
client_configure(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: adding to list");
|
|
|
|
|
client_add_to_list(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: adding to workspace");
|
|
|
|
|
workspace_add_client(dwn->current_workspace, client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: setting EWMH");
|
|
|
|
|
atoms_set_window_desktop(window, client->workspace);
|
|
|
|
|
atoms_update_client_list();
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: verifying window again");
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, window, &wa)) {
|
|
|
|
|
LOG_WARN("client_manage: window %lu disappeared before event setup", window);
|
|
|
|
|
client_sync_log("client_manage: window gone before events");
|
|
|
|
|
workspace_remove_client(client->workspace, client);
|
|
|
|
|
client_remove_from_list(client);
|
|
|
|
|
client_destroy(client);
|
|
|
|
|
atoms_update_client_list();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: selecting input");
|
|
|
|
|
XSelectInput(dwn->display, window,
|
|
|
|
|
EnterWindowMask | FocusChangeMask | PropertyChangeMask |
|
|
|
|
|
StructureNotifyMask);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: grabbing button");
|
|
|
|
|
XGrabButton(dwn->display, Button1, AnyModifier, window, False,
|
|
|
|
|
ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: syncing X");
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: showing");
|
|
|
|
|
client_show(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: focusing");
|
|
|
|
|
client_focus(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_manage: DONE");
|
|
|
|
|
return client;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_unmanage(Client *client)
|
|
|
|
|
{
|
|
|
|
|
client_sync_log("client_unmanage: START");
|
|
|
|
|
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
client_sync_log("client_unmanage: NULL client");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("Unmanaging window: %lu", client->window);
|
|
|
|
|
|
2025-12-28 10:23:03 +01:00
|
|
|
client_sync_log("client_unmanage: remove from MRU");
|
|
|
|
|
workspace_mru_remove(client->workspace, client);
|
|
|
|
|
|
2025-12-28 03:14:31 +01:00
|
|
|
client_sync_log("client_unmanage: remove from workspace");
|
|
|
|
|
workspace_remove_client(client->workspace, client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_unmanage: remove from list");
|
|
|
|
|
client_remove_from_list(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_unmanage: reparent from frame");
|
|
|
|
|
client_reparent_from_frame(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_unmanage: update EWMH");
|
|
|
|
|
atoms_update_client_list();
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_unmanage: destroy client");
|
|
|
|
|
client_destroy(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_unmanage: focus next");
|
2025-12-28 10:23:03 +01:00
|
|
|
XGrabServer(dwn->display);
|
|
|
|
|
|
2025-12-28 03:14:31 +01:00
|
|
|
Workspace *ws = workspace_get_current();
|
|
|
|
|
if (ws != NULL && ws->focused == NULL) {
|
2025-12-28 10:23:03 +01:00
|
|
|
Client *next = workspace_mru_get_previous(dwn->current_workspace, NULL);
|
2025-12-28 03:14:31 +01:00
|
|
|
if (next != NULL) {
|
|
|
|
|
client_focus(next);
|
2025-12-28 10:23:03 +01:00
|
|
|
} else {
|
|
|
|
|
XSetInputFocus(dwn->display, dwn->root, RevertToPointerRoot, CurrentTime);
|
|
|
|
|
atoms_set_active_window(None);
|
2025-12-28 03:14:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 10:23:03 +01:00
|
|
|
XUngrabServer(dwn->display);
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
|
2025-12-28 03:14:31 +01:00
|
|
|
client_sync_log("client_unmanage: DONE");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *client_find_by_window(Window window)
|
|
|
|
|
{
|
|
|
|
|
if (dwn == NULL || window == None) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
|
|
|
|
|
if (c->window == window) {
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *client_find_by_frame(Window frame)
|
|
|
|
|
{
|
|
|
|
|
if (dwn == NULL || frame == None) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
|
|
|
|
|
if (c->frame == frame) {
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void client_focus(Client *client)
|
|
|
|
|
{
|
|
|
|
|
client_sync_log("client_focus: START");
|
|
|
|
|
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
client_sync_log("client_focus: NULL check failed");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
LOG_WARN("client_focus: client has invalid window (None)");
|
|
|
|
|
client_sync_log("client_focus: window is None");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_WARN("client_focus: window %lu no longer exists", client->window);
|
|
|
|
|
client_sync_log("client_focus: window gone");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_focus: unfocusing previous");
|
|
|
|
|
|
|
|
|
|
Workspace *ws = workspace_get(client->workspace);
|
|
|
|
|
if (ws != NULL && ws->focused != NULL && ws->focused != client) {
|
|
|
|
|
client_unfocus(ws->focused);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_focus: XSetInputFocus");
|
|
|
|
|
|
|
|
|
|
XSetInputFocus(dwn->display, client->window, RevertToPointerRoot, CurrentTime);
|
2025-12-28 05:01:46 +01:00
|
|
|
XSync(dwn->display, False);
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
client_sync_log("client_focus: updating workspace");
|
|
|
|
|
|
|
|
|
|
if (ws != NULL) {
|
|
|
|
|
ws->focused = client;
|
2025-12-28 10:23:03 +01:00
|
|
|
workspace_mru_push(client->workspace, client);
|
2025-12-28 03:14:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_focus: raising");
|
|
|
|
|
|
|
|
|
|
client_raise(client);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_focus: decorations");
|
|
|
|
|
|
|
|
|
|
decorations_render(client, true);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_focus: EWMH");
|
|
|
|
|
|
|
|
|
|
atoms_set_active_window(client->window);
|
|
|
|
|
|
|
|
|
|
client_sync_log("client_focus: DONE");
|
|
|
|
|
LOG_DEBUG("Focused window: %s", client->title);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_unfocus(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decorations_render(client, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_raise(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Window win = (client->frame != None) ? client->frame : client->window;
|
|
|
|
|
if (win == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XRaiseWindow(dwn->display, win);
|
|
|
|
|
|
|
|
|
|
notifications_raise_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_lower(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Window win = (client->frame != None) ? client->frame : client->window;
|
|
|
|
|
if (win == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
XLowerWindow(dwn->display, win);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_minimize(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->flags |= CLIENT_MINIMIZED;
|
|
|
|
|
client_hide(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_restore(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->flags &= ~CLIENT_MINIMIZED;
|
|
|
|
|
client_show(client);
|
|
|
|
|
client_focus(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void client_move(Client *client, int x, int y)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->x = x;
|
|
|
|
|
client->y = y;
|
|
|
|
|
client_configure(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_resize(Client *client, int width, int height)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_apply_size_hints(client, &width, &height);
|
|
|
|
|
client->width = width;
|
|
|
|
|
client->height = height;
|
|
|
|
|
client_configure(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_move_resize(Client *client, int x, int y, int width, int height)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_apply_size_hints(client, &width, &height);
|
|
|
|
|
client->x = x;
|
|
|
|
|
client->y = y;
|
|
|
|
|
client->width = width;
|
|
|
|
|
client->height = height;
|
|
|
|
|
client_configure(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_configure(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_DEBUG("client_configure: window no longer exists");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int title_height = (dwn->config && dwn->config->show_decorations) ?
|
|
|
|
|
config_get_title_height() : 0;
|
|
|
|
|
int border = client->border_width;
|
|
|
|
|
|
|
|
|
|
if (client->frame != None) {
|
|
|
|
|
XMoveResizeWindow(dwn->display, client->frame,
|
|
|
|
|
client->x - border,
|
|
|
|
|
client->y - title_height - border,
|
|
|
|
|
client->width + 2 * border,
|
|
|
|
|
client->height + title_height + 2 * border);
|
|
|
|
|
|
|
|
|
|
XMoveResizeWindow(dwn->display, client->window,
|
|
|
|
|
border, title_height + border,
|
|
|
|
|
client->width, client->height);
|
|
|
|
|
} else {
|
|
|
|
|
XMoveResizeWindow(dwn->display, client->window,
|
|
|
|
|
client->x, client->y,
|
|
|
|
|
client->width, client->height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XConfigureEvent ce;
|
2025-12-28 05:01:46 +01:00
|
|
|
memset(&ce, 0, sizeof(ce));
|
2025-12-28 03:14:31 +01:00
|
|
|
ce.type = ConfigureNotify;
|
|
|
|
|
ce.event = client->window;
|
|
|
|
|
ce.window = client->window;
|
|
|
|
|
ce.x = client->x;
|
|
|
|
|
ce.y = client->y;
|
|
|
|
|
ce.width = client->width;
|
|
|
|
|
ce.height = client->height;
|
|
|
|
|
ce.border_width = 0;
|
|
|
|
|
ce.above = None;
|
|
|
|
|
ce.override_redirect = False;
|
|
|
|
|
|
|
|
|
|
XSendEvent(dwn->display, client->window, False, StructureNotifyMask, (XEvent *)&ce);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_apply_size_hints(Client *client, int *width, int *height)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XSizeHints hints;
|
|
|
|
|
long supplied;
|
|
|
|
|
|
|
|
|
|
if (!XGetWMNormalHints(dwn->display, client->window, &hints, &supplied)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hints.flags & PMinSize) {
|
|
|
|
|
if (*width < hints.min_width) *width = hints.min_width;
|
|
|
|
|
if (*height < hints.min_height) *height = hints.min_height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hints.flags & PMaxSize) {
|
|
|
|
|
if (*width > hints.max_width) *width = hints.max_width;
|
|
|
|
|
if (*height > hints.max_height) *height = hints.max_height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hints.flags & PResizeInc) {
|
|
|
|
|
int base_w = (hints.flags & PBaseSize) ? hints.base_width : 0;
|
|
|
|
|
int base_h = (hints.flags & PBaseSize) ? hints.base_height : 0;
|
|
|
|
|
|
|
|
|
|
*width = base_w + ((*width - base_w) / hints.width_inc) * hints.width_inc;
|
|
|
|
|
*height = base_h + ((*height - base_h) / hints.height_inc) * hints.height_inc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void client_update_title(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *name = atoms_get_window_name(client->window);
|
|
|
|
|
if (name != NULL) {
|
|
|
|
|
strncpy(client->title, name, sizeof(client->title) - 1);
|
|
|
|
|
client->title[sizeof(client->title) - 1] = '\0';
|
|
|
|
|
dwn_free(name);
|
|
|
|
|
} else {
|
|
|
|
|
strncpy(client->title, "Untitled", sizeof(client->title) - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->frame != None) {
|
|
|
|
|
Workspace *ws = workspace_get(client->workspace);
|
|
|
|
|
bool focused = (ws != NULL && ws->focused == client);
|
|
|
|
|
decorations_render(client, focused);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_update_class(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
atoms_get_wm_class(client->window, client->class, NULL, sizeof(client->class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_set_fullscreen(Client *client, bool fullscreen)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_WARN("client_set_fullscreen: window %lu no longer exists", client->window);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fullscreen) {
|
|
|
|
|
if (!(client->flags & CLIENT_FULLSCREEN)) {
|
|
|
|
|
client->old_x = client->x;
|
|
|
|
|
client->old_y = client->y;
|
|
|
|
|
client->old_width = client->width;
|
|
|
|
|
client->old_height = client->height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->flags |= CLIENT_FULLSCREEN;
|
2025-12-28 09:26:53 +01:00
|
|
|
atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_FULLSCREEN, true);
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
client->x = 0;
|
|
|
|
|
client->y = 0;
|
|
|
|
|
client->width = dwn->screen_width;
|
|
|
|
|
client->height = dwn->screen_height;
|
|
|
|
|
|
|
|
|
|
if (client->frame != None) {
|
|
|
|
|
XUnmapWindow(dwn->display, client->frame);
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
XReparentWindow(dwn->display, client->window, dwn->root, 0, 0);
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
}
|
|
|
|
|
XMoveResizeWindow(dwn->display, client->window,
|
|
|
|
|
0, 0, client->width, client->height);
|
|
|
|
|
XMapWindow(dwn->display, client->window);
|
|
|
|
|
XRaiseWindow(dwn->display, client->window);
|
2025-12-28 09:26:53 +01:00
|
|
|
XSync(dwn->display, False);
|
2025-12-28 03:14:31 +01:00
|
|
|
} else {
|
|
|
|
|
client->flags &= ~CLIENT_FULLSCREEN;
|
2025-12-28 09:26:53 +01:00
|
|
|
atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_FULLSCREEN, false);
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
client->x = client->old_x;
|
|
|
|
|
client->y = client->old_y;
|
|
|
|
|
client->width = client->old_width;
|
|
|
|
|
client->height = client->old_height;
|
|
|
|
|
|
|
|
|
|
if (client->frame != None) {
|
|
|
|
|
int title_height = config_get_title_height();
|
|
|
|
|
int border = client->border_width;
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
XReparentWindow(dwn->display, client->window, client->frame,
|
|
|
|
|
border, title_height + border);
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
XMapWindow(dwn->display, client->frame);
|
|
|
|
|
}
|
|
|
|
|
client_configure(client);
|
2025-12-28 09:26:53 +01:00
|
|
|
decorations_render(client, true);
|
|
|
|
|
client_raise(client);
|
|
|
|
|
XSync(dwn->display, False);
|
2025-12-28 03:14:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_toggle_fullscreen(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
client_set_fullscreen(client, !(client->flags & CLIENT_FULLSCREEN));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_set_floating(Client *client, bool floating)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (floating) {
|
|
|
|
|
client->flags |= CLIENT_FLOATING;
|
|
|
|
|
} else {
|
|
|
|
|
client->flags &= ~CLIENT_FLOATING;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_toggle_floating(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
client_set_floating(client, !(client->flags & CLIENT_FLOATING));
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 10:23:03 +01:00
|
|
|
bool client_is_maximized(Client *client)
|
|
|
|
|
{
|
|
|
|
|
return client != NULL && (client->flags & CLIENT_MAXIMIZED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_set_maximize(Client *client, bool maximized)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_WARN("client_set_maximize: window %lu no longer exists", client->window);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maximized) {
|
|
|
|
|
if (!(client->flags & CLIENT_MAXIMIZED)) {
|
|
|
|
|
client->old_x = client->x;
|
|
|
|
|
client->old_y = client->y;
|
|
|
|
|
client->old_width = client->width;
|
|
|
|
|
client->old_height = client->height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->flags |= CLIENT_MAXIMIZED;
|
|
|
|
|
client->flags |= CLIENT_FLOATING;
|
|
|
|
|
|
|
|
|
|
atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_MAXIMIZED_VERT, true);
|
|
|
|
|
atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_MAXIMIZED_HORZ, true);
|
|
|
|
|
|
|
|
|
|
int area_x, area_y, area_width, area_height;
|
|
|
|
|
layout_get_usable_area(&area_x, &area_y, &area_width, &area_height);
|
|
|
|
|
|
|
|
|
|
int gap = config_get_gap();
|
|
|
|
|
int title_height = config_get_title_height();
|
|
|
|
|
int border = client->border_width;
|
|
|
|
|
|
|
|
|
|
client->x = area_x + gap + border;
|
|
|
|
|
client->y = area_y + gap + title_height + border;
|
|
|
|
|
client->width = area_width - 2 * gap - 2 * border;
|
|
|
|
|
client->height = area_height - 2 * gap - title_height - 2 * border;
|
|
|
|
|
|
|
|
|
|
client_configure(client);
|
|
|
|
|
decorations_render(client, true);
|
|
|
|
|
client_raise(client);
|
|
|
|
|
} else {
|
|
|
|
|
client->flags &= ~CLIENT_MAXIMIZED;
|
|
|
|
|
|
|
|
|
|
atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_MAXIMIZED_VERT, false);
|
|
|
|
|
atoms_update_wm_state(client->window, ewmh.NET_WM_STATE_MAXIMIZED_HORZ, false);
|
|
|
|
|
|
|
|
|
|
client->x = client->old_x;
|
|
|
|
|
client->y = client->old_y;
|
|
|
|
|
client->width = client->old_width;
|
|
|
|
|
client->height = client->old_height;
|
|
|
|
|
|
|
|
|
|
client_configure(client);
|
|
|
|
|
decorations_render(client, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_toggle_maximize(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
client_set_maximize(client, !(client->flags & CLIENT_MAXIMIZED));
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
bool client_is_floating(Client *client)
|
|
|
|
|
{
|
|
|
|
|
return client != NULL && (client->flags & CLIENT_FLOATING);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool client_is_fullscreen(Client *client)
|
|
|
|
|
{
|
|
|
|
|
return client != NULL && (client->flags & CLIENT_FULLSCREEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool client_is_minimized(Client *client)
|
|
|
|
|
{
|
|
|
|
|
return client != NULL && (client->flags & CLIENT_MINIMIZED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool client_is_dialog(Window window)
|
|
|
|
|
{
|
|
|
|
|
Atom type;
|
|
|
|
|
if (atoms_get_window_type(window, &type)) {
|
|
|
|
|
return type == ewmh.NET_WM_WINDOW_TYPE_DIALOG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Window transient_for = None;
|
|
|
|
|
if (XGetTransientForHint(dwn->display, window, &transient_for)) {
|
|
|
|
|
return transient_for != None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool client_is_dock(Window window)
|
|
|
|
|
{
|
|
|
|
|
Atom type;
|
|
|
|
|
if (atoms_get_window_type(window, &type)) {
|
|
|
|
|
return type == ewmh.NET_WM_WINDOW_TYPE_DOCK;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool client_is_desktop(Window window)
|
|
|
|
|
{
|
|
|
|
|
Atom type;
|
|
|
|
|
if (atoms_get_window_type(window, &type)) {
|
|
|
|
|
return type == ewmh.NET_WM_WINDOW_TYPE_DESKTOP;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void client_create_frame(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
LOG_ERROR("client_create_frame: invalid parameters");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!dwn->config || !dwn->config->show_decorations) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
LOG_ERROR("client_create_frame: client has no valid window");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
if (frame_width <= 0 || frame_height <= 0) {
|
|
|
|
|
LOG_ERROR("client_create_frame: invalid dimensions (%dx%d)", frame_width, frame_height);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XSetWindowAttributes swa;
|
2025-12-28 05:01:46 +01:00
|
|
|
memset(&swa, 0, sizeof(swa));
|
2025-12-28 03:14:31 +01:00
|
|
|
swa.override_redirect = True;
|
|
|
|
|
swa.background_pixel = dwn->config->colors.title_unfocused_bg;
|
|
|
|
|
swa.border_pixel = dwn->config->colors.border_unfocused;
|
|
|
|
|
swa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
|
|
|
|
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
|
|
|
|
|
ExposureMask | EnterWindowMask;
|
|
|
|
|
|
|
|
|
|
client->frame = XCreateWindow(dwn->display, dwn->root,
|
|
|
|
|
client->x - border,
|
|
|
|
|
client->y - title_height - border,
|
|
|
|
|
frame_width,
|
|
|
|
|
frame_height,
|
|
|
|
|
0,
|
|
|
|
|
CopyFromParent, InputOutput, CopyFromParent,
|
|
|
|
|
CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask,
|
|
|
|
|
&swa);
|
|
|
|
|
|
|
|
|
|
if (client->frame == None) {
|
|
|
|
|
LOG_ERROR("client_create_frame: XCreateWindow failed for window %lu", client->window);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XSetWindowBorderWidth(dwn->display, client->frame, border);
|
2025-12-28 05:01:46 +01:00
|
|
|
XSync(dwn->display, False);
|
2025-12-28 03:14:31 +01:00
|
|
|
|
|
|
|
|
LOG_DEBUG("Created frame %lu for window %lu", client->frame, client->window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_destroy_frame(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || client->frame == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XDestroyWindow(dwn->display, client->frame);
|
|
|
|
|
client->frame = None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_reparent_to_frame(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || client->frame == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
LOG_WARN("client_reparent_to_frame: client has no valid window");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_WARN("client_reparent_to_frame: window %lu no longer exists", client->window);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int title_height = config_get_title_height();
|
|
|
|
|
int border = client->border_width;
|
|
|
|
|
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
|
|
|
|
|
XReparentWindow(dwn->display, client->window, client->frame,
|
|
|
|
|
border, title_height + border);
|
|
|
|
|
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
|
|
|
|
|
XSaveContext(dwn->display, client->window, XUniqueContext(), (XPointer)client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_reparent_from_frame(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->frame == None || client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_WARN("client_reparent_from_frame: window %lu no longer exists", client->window);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
|
|
|
|
|
XReparentWindow(dwn->display, client->window, dwn->root,
|
|
|
|
|
client->x, client->y);
|
|
|
|
|
|
|
|
|
|
XSync(dwn->display, False);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void client_show(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_DEBUG("client_show: window no longer exists");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->frame != None) {
|
|
|
|
|
XMapWindow(dwn->display, client->frame);
|
|
|
|
|
}
|
|
|
|
|
XMapWindow(dwn->display, client->window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_hide(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_DEBUG("client_hide: window no longer exists");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->frame != None) {
|
|
|
|
|
XUnmapWindow(dwn->display, client->frame);
|
|
|
|
|
}
|
|
|
|
|
XUnmapWindow(dwn->display, client->window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool client_is_visible(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->flags & CLIENT_MINIMIZED) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(client->flags & CLIENT_STICKY) &&
|
|
|
|
|
client->workspace != (unsigned int)dwn->current_workspace) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void client_close(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XWindowAttributes wa;
|
|
|
|
|
if (!XGetWindowAttributes(dwn->display, client->window, &wa)) {
|
|
|
|
|
LOG_DEBUG("client_close: window no longer exists");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (atoms_window_supports_protocol(client->window, icccm.WM_DELETE_WINDOW)) {
|
|
|
|
|
atoms_send_protocol(client->window, icccm.WM_DELETE_WINDOW, CurrentTime);
|
|
|
|
|
} else {
|
|
|
|
|
client_kill(client);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_kill(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL || dwn->display == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->window == None) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XKillClient(dwn->display, client->window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void client_add_to_list(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->next = dwn->client_list;
|
|
|
|
|
client->prev = NULL;
|
|
|
|
|
|
|
|
|
|
if (dwn->client_list != NULL) {
|
|
|
|
|
dwn->client_list->prev = client;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dwn->client_list = client;
|
|
|
|
|
dwn->client_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void client_remove_from_list(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL || dwn == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->prev != NULL) {
|
|
|
|
|
client->prev->next = client->next;
|
|
|
|
|
} else {
|
|
|
|
|
dwn->client_list = client->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->next != NULL) {
|
|
|
|
|
client->next->prev = client->prev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dwn->client_count--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int client_count(void)
|
|
|
|
|
{
|
|
|
|
|
return dwn != NULL ? dwn->client_count : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int client_count_on_workspace(int workspace)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
for (Client *c = dwn->client_list; c != NULL; c = c->next) {
|
|
|
|
|
if (c->workspace == (unsigned int)workspace) {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Client *client_get_next(Client *client)
|
|
|
|
|
{
|
|
|
|
|
return client != NULL ? client->next : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *client_get_prev(Client *client)
|
|
|
|
|
{
|
|
|
|
|
return client != NULL ? client->prev : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *client_get_first(void)
|
|
|
|
|
{
|
|
|
|
|
return dwn != NULL ? dwn->client_list : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *client_get_last(void)
|
|
|
|
|
{
|
|
|
|
|
if (dwn == NULL || dwn->client_list == NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client *c = dwn->client_list;
|
|
|
|
|
while (c->next != NULL) {
|
|
|
|
|
c = c->next;
|
|
|
|
|
}
|
|
|
|
|
return c;
|
|
|
|
|
}
|