void window_unfocus(Window window) { HSDebug("window_unfocus NORMAL\n"); window_update_border(window, g_window_border_normal_color); HSClient* c = get_client_from_window(window); if (c) { if (c->urgent) { HSDebug("window_unfocus URGENT\n"); window_update_border(window, g_window_border_urgent_color); } grab_client_buttons(c, false); } }
void client_setup_border(HSClient* client, bool focused) { unsigned long colors[] = { g_window_border_normal_color, g_window_border_active_color, }; if (client->urgent) { HSDebug("client_setup_border URGENT\n"); window_update_border(client->window, g_window_border_urgent_color); } else { HSDebug("client_setup_border %s\n", (focused ? "ACTIVE" : "NORMAL")); window_update_border(client->window, colors[focused ? 1 : 0]); } }
void mapnotify(XEvent* event) { HSDebug("name is: MapNotify\n"); HSClient* c; if ((c = get_client_from_window(event->xmap.window))) { // reset focus. so a new window gets the focus if it shall have the // input focus frame_focus_recursive(g_cur_frame); // also update the window title - just to be sure client_update_title(c); } }
void client_resize_fullscreen(HSClient* client, HSMonitor* m) { if (!client || !m) { HSDebug("client_resize_fullscreen() got invalid parameters\n"); return; } XSetWindowBorderWidth(g_display, client->window, 0); client->last_size = m->rect; client->last_border_width = 0; XMoveResizeWindow(g_display, client->window, m->rect.x, m->rect.y, m->rect.width, m->rect.height); }
void client_resize_floating(HSClient* client, HSMonitor* m) { if (!client || !m) return; if (client->fullscreen) { client_resize_fullscreen(client, m); return; } // ensure minimal size if (client->float_size.width < WINDOW_MIN_WIDTH) client->float_size.width = WINDOW_MIN_WIDTH; if (client->float_size.height < WINDOW_MIN_HEIGHT) client->float_size.height = WINDOW_MIN_HEIGHT; bool border_changed = false; if (client->last_border_width != *g_window_border_width) { client->last_border_width = *g_window_border_width; border_changed = true; } client->last_size = client->float_size; client->last_size.x += m->rect.x + m->pad_left; client->last_size.y += m->rect.y + m->pad_up; // ensure position is on monitor int space = g_monitor_float_treshold; client->last_size.x = CLAMP(client->last_size.x, m->rect.x + m->pad_left - client->last_size.width + space, m->rect.x + m->rect.width - m->pad_left - m->pad_right - space); client->last_size.y = CLAMP(client->last_size.y, m->rect.y + m->pad_up - client->last_size.height + space, m->rect.y + m->rect.height - m->pad_up - m->pad_down - space); XRectangle rect = client->last_size; // add window border to last_size client->last_size.width += 2 * client->last_border_width; client->last_size.height += 2 * client->last_border_width; if (border_changed) { XSetWindowBorderWidth(g_display, client->window, *g_window_border_width); } XMoveResizeWindow(g_display, client->window, rect.x, rect.y, rect.width, rect.height); if (*g_window_border_inner_width > 0 && *g_window_border_inner_width < *g_window_border_width) { unsigned long current_border_color = get_window_border_color(client); HSDebug("client_resize %s\n", current_border_color == g_window_border_active_color ? "ACTIVE" : "NORMAL"); set_window_double_border(g_display, client->window, *g_window_border_inner_width, g_window_border_inner_color, current_border_color); } }
static void ewmh_add_tag_stack(HSTag* tag, void* data) { struct ewmhstack* stack = (struct ewmhstack*)data; if (find_monitor_with_tag(tag)) { // do not add tags because they are already added return; } int remain; stack_to_window_buf(tag->stack, stack->buf + stack->i, stack->count - stack->i, true, &remain); if (remain >= 0) { stack->i = stack->count - remain; } else { HSDebug("Warning: not enough space in the ewmh stack\n"); stack->i = stack->count; } }
void buttonpress(XEvent* event) { XButtonEvent* be = &(event->xbutton); HSDebug("name is: ButtonPress on sub %lx, win %lx\n", be->subwindow, be->window); if (mouse_binding_find(be->state, be->button)) { mouse_start_drag(event); } else { focus_window(be->window, false, true); if (*g_raise_on_click) { HSClient* client = get_client_from_window(be->window); if (client) { client_raise(client); } } } XAllowEvents(g_display, ReplayPointer, be->time); }
void enternotify(XEvent* event) { XCrossingEvent *ce = &event->xcrossing; HSDebug("name is: EnterNotify, focus = %d\n", event->xcrossing.focus); if (!mouse_is_dragging() && *g_focus_follows_mouse && false == ce->focus) { HSClient* c = get_client_from_window(ce->window); HSFrame* target; if (c && c->tag->floating == false && (target = find_frame_with_window(c->tag->frame, ce->window)) && target->content.clients.layout == LAYOUT_MAX && frame_focused_window(target) != ce->window) { // don't allow focus_follows_mouse if another window would be // hidden during that focus change (which only occurs in max layout) } else { focus_window(ce->window, false, true); } } }
void window_focus(Window window) { HSClient* client = get_client_from_window(window); assert(client != NULL); // set keyboard focus if (!client->neverfocus) { XSetInputFocus(g_display, window, RevertToPointerRoot, CurrentTime); } else client_sendevent(client, g_wmatom[WMTakeFocus]); if (window != lastfocus) { /* FIXME: this is a workaround because window_focus always is called * twice. see BUGS for more information * * only emit the hook if the focus *really* changes */ // unfocus last one window_unfocus(lastfocus); hsobject_link(g_client_object, &client->object, "focus"); ewmh_update_active_window(window); tag_update_each_focus_layer(); char* title = client ? client->title->str : "?"; char winid_str[STRING_BUF_SIZE]; snprintf(winid_str, STRING_BUF_SIZE, "0x%x", (unsigned int)window); hook_emit_list("focus_changed", winid_str, title, NULL); } // change window-colors HSDebug("window_focus ACTIVE\n"); window_update_border(window, g_window_border_active_color); lastfocus = window; /* do some specials for the max layout */ bool is_max_layout = frame_focused_window(g_cur_frame) == window && g_cur_frame->content.clients.layout == LAYOUT_MAX && get_current_monitor()->tag->floating == false; if (*g_raise_on_focus || is_max_layout) { client_raise(client); } tag_update_focus_layer(get_current_monitor()->tag); grab_client_buttons(get_client_from_window(window), true); client_set_urgent(client, false); }
void maprequest(XEvent* event) { HSDebug("name is: MapRequest\n"); XMapRequestEvent* mapreq = &event->xmaprequest; if (is_herbstluft_window(g_display, mapreq->window)) { // just map the window if it wants that XWindowAttributes wa; if (!XGetWindowAttributes(g_display, mapreq->window, &wa)) { return; } XMapWindow(g_display, mapreq->window); } else if (!get_client_from_window(mapreq->window)) { // client should be managed (is not ignored) // but is not managed yet HSClient* client = manage_client(mapreq->window); if (client && find_monitor_with_tag(client->tag)) { XMapWindow(g_display, mapreq->window); } } // else: ignore all other maprequests from windows // that are managed already }
void execute_autostart_file() { GString* path = NULL; if (g_autostart_path) { path = g_string_new(g_autostart_path); } else { // find right directory char* xdg_config_home = getenv("XDG_CONFIG_HOME"); if (xdg_config_home) { path = g_string_new(xdg_config_home); } else { char* home = getenv("HOME"); if (!home) { g_warning("Will not run autostart file. " "Neither $HOME or $XDG_CONFIG_HOME is set.\n"); return; } path = g_string_new(home); g_string_append_c(path, G_DIR_SEPARATOR); g_string_append(path, ".config"); } g_string_append_c(path, G_DIR_SEPARATOR); g_string_append(path, HERBSTLUFT_AUTOSTART); } if (0 == fork()) { if (g_display) { close(ConnectionNumber(g_display)); } setsid(); execl(path->str, path->str, NULL); char* global_autostart = HERBSTLUFT_GLOBAL_AUTOSTART; HSDebug("Can not execute %s, falling back to %s\n", path->str, global_autostart); execl(global_autostart, global_autostart, NULL); fprintf(stderr, "herbstluftwm: execvp \"%s\"", global_autostart); perror(" failed"); exit(EXIT_FAILURE); } g_string_free(path, true); }
/* There's no way to check accesses to destroyed windows, thus those cases are * ignored (especially on UnmapNotify's). Other types of errors call Xlibs * default error handler, which may call exit. */ int xerror(Display *dpy, XErrorEvent *ee) { if(ee->error_code == BadWindow || ee->error_code == BadGC || ee->error_code == BadPixmap || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) { return 0; } fprintf(stderr, "herbstluftwm: fatal error: request code=%d, error code=%d\n", ee->request_code, ee->error_code); if (ee->error_code == BadDrawable) { HSDebug("Warning: ignoring X_BadDrawable"); return 0; } return g_xerrorxlib(dpy, ee); /* may call exit */ }
void client_update_title(HSClient* client) { GString* new_name = window_property_to_g_string(g_display, client->window, g_netatom[NetWmName]); if (!new_name) { char* ch_new_name = NULL; /* if ewmh name isn't set, then fall back to WM_NAME */ if (0 != XFetchName(g_display, client->window, &ch_new_name)) { new_name = g_string_new(ch_new_name); XFree(ch_new_name); } else { new_name = g_string_new(""); HSDebug("no title for window %lx found, using \"\"\n", client->window); } } bool changed = (0 != strcmp(client->title->str, new_name->str)); g_string_free(client->title, true); client->title = new_name; if (changed && get_current_client() == client) { char buf[STRING_BUF_SIZE]; snprintf(buf, STRING_BUF_SIZE, "0x%lx", client->window); hook_emit_list("window_title_changed", buf, client->title->str, NULL); } }
void keypress(XEvent* event) { HSDebug("name is: KeyPress\n"); handle_key_press(event); }
void client_resize(HSClient* client, XRectangle rect, HSFrame* frame) { // ensure minimum size if (rect.width < WINDOW_MIN_WIDTH) { rect.width = WINDOW_MIN_WIDTH; } if (rect.height < WINDOW_MIN_HEIGHT) { rect.height = WINDOW_MIN_HEIGHT; } if (!client) { HSDebug("Warning: client_resize(NULL, ...) was called\n"); return; } Window win = client->window; if (client->pseudotile) { XRectangle size = client->float_size; // floating sizes don't include window border, tiling sizes do. // so convert the floating size to a tiling size size.width += *g_window_border_width * 2; size.height += *g_window_border_width * 2; // ensure size is not larger than rect-tile size.width = MIN(size.width, rect.width); size.height = MIN(size.height, rect.height); // center it rect.x = rect.x + rect.width/2 - size.width/2; rect.y = rect.y + rect.height/2 - size.height/2; rect.width = size.width; rect.height = size.height; } int border_width = *g_window_border_width; if (*g_smart_window_surroundings && !client->pseudotile && (frame->content.clients.count == 1 || frame->content.clients.layout == LAYOUT_MAX)) { border_width = 0; } if (RECTANGLE_EQUALS(client->last_size, rect) && client->last_border_width == border_width) { return; } client->last_size = rect; client->last_border_width = border_width; // apply border width rect.width -= border_width * 2; rect.height -= border_width * 2; if (!*g_smart_window_surroundings || (frame->content.clients.count != 1 && frame->content.clients.layout != LAYOUT_MAX)) { // apply window gap rect.width -= *g_window_gap; rect.height -= *g_window_gap; } XSetWindowBorderWidth(g_display, win, border_width); XMoveResizeWindow(g_display, win, rect.x, rect.y, rect.width, rect.height); if (*g_window_border_inner_width > 0 && *g_window_border_inner_width < *g_window_border_width) { unsigned long current_border_color = get_window_border_color(client); HSDebug("client_resize %s\n", current_border_color == g_window_border_active_color ? "ACTIVE" : "NORMAL"); set_window_double_border(g_display, win, *g_window_border_inner_width, g_window_border_inner_color, current_border_color); } //// send new size to client //// WHY SHOULD I? -> faster? only one call? //XConfigureEvent ce; //ce.type = ConfigureNotify; //ce.display = g_display; //ce.event = win; //ce.window = win; //ce.x = rect.x; //ce.y = rect.y; //ce.width = rect.width; //ce.height = rect.height; //ce.border_width = 0; //ce.above = None; //ce.override_redirect = False; //XSendEvent(g_display, win, False, StructureNotifyMask, (XEvent *)&ce); }
static void handle_signal(int signal) { HSDebug("Interrupted by signal %d\n", signal); g_aboutToQuit = true; return; }
void configurerequest(XEvent* event) { HSDebug("name is: ConfigureRequest\n"); event_on_configure(*event); }
void buttonrelease(XEvent* event) { HSDebug("name is: ButtonRelease\n"); mouse_stop_drag(); }
void expose(XEvent* event) { HSDebug("name is: Expose\n"); }
void focusin(XEvent* event) { HSDebug("name is: FocusIn\n"); }
int main(int argc, char* argv[]) { parse_arguments(argc, argv); if(!(g_display = XOpenDisplay(NULL))) die("herbstluftwm: cannot open display\n"); checkotherwm(); // remove zombies on SIGCHLD sigaction_signal(SIGCHLD, remove_zombies); sigaction_signal(SIGINT, handle_signal); sigaction_signal(SIGQUIT, handle_signal); sigaction_signal(SIGTERM, handle_signal); // set some globals g_screen = DefaultScreen(g_display); g_screen_width = DisplayWidth(g_display, g_screen); g_screen_height = DisplayHeight(g_display, g_screen); g_root = RootWindow(g_display, g_screen); XSelectInput(g_display, g_root, ROOT_EVENT_MASK); // initialize subsystems for (int i = 0; i < LENGTH(g_modules); i++) { g_modules[i].init(); } fetch_settings(); // setup ensure_monitors_are_available(); scan(); tag_force_update_flags(); all_monitors_apply_layout(); ewmh_update_all(); execute_autostart_file(); // main loop XEvent event; int x11_fd; fd_set in_fds; x11_fd = ConnectionNumber(g_display); while (!g_aboutToQuit) { FD_ZERO(&in_fds); FD_SET(x11_fd, &in_fds); // wait for an event or a signal select(x11_fd + 1, &in_fds, 0, 0, NULL); if (g_aboutToQuit) { break; } while (XPending(g_display)) { XNextEvent(g_display, &event); void (*handler) (XEvent*) = g_default_handler[event.type]; if (handler != NULL) { handler(&event); } } } // destroy all subsystems for (int i = LENGTH(g_modules); i --> 0;) { g_modules[i].destroy(); } XCloseDisplay(g_display); // check if we shall restart an other window manager if (g_exec_before_quit) { if (g_exec_args) { // do actual exec HSDebug("==> Doing wmexec to %s\n", g_exec_args[0]); execvp(g_exec_args[0], g_exec_args); fprintf(stderr, "herbstluftwm: execvp \"%s\"", g_exec_args[0]); perror(" failed"); } // on failure or if no other wm given, then fall back HSDebug("==> Doing wmexec to %s\n", argv[0]); execvp(argv[0], argv); fprintf(stderr, "herbstluftwm: execvp \"%s\"", argv[1]); perror(" failed"); return EXIT_FAILURE; } return EXIT_SUCCESS; }
void unmapnotify(XEvent* event) { HSDebug("name is: UnmapNotify for %lx\n", event->xunmap.window); unmanage_client(event->xunmap.window); }
void unmapnotify(XEvent* event) { HSDebug("name is: UnmapNotify for %lx\n", event->xunmap.window); if (!clientlist_ignore_unmapnotify(event->xunmap.window)) { unmanage_client(event->xunmap.window); } }
void ewmh_handle_client_message(XEvent* event) { HSDebug("Received event: ClientMessage\n"); XClientMessageEvent* me = &(event->xclient); int index; for (index = 0; index < NetCOUNT; index++) { if (me->message_type == g_netatom[index]) { break; } } if (index >= NetCOUNT) { HSDebug("received unknown client message\n"); return; } HSClient* client; int desktop_index; switch (index) { case NetActiveWindow: // only steal focus it allowed to the current source // (i.e. me->data.l[0] in this case as specified by EWMH) if (focus_stealing_allowed(me->data.l[0])) { focus_window(me->window, true, true); } break; case NetCurrentDesktop: desktop_index = me->data.l[0]; if (desktop_index < 0 || desktop_index >= g_tags->len) { HSDebug("_NET_CURRENT_DESKTOP: invalid index \"%d\"\n", desktop_index); break; } HSTag* tag = g_array_index(g_tags, HSTag*, desktop_index); monitor_set_tag(get_current_monitor(), tag); break; case NetWmDesktop: desktop_index = me->data.l[0]; if (!focus_stealing_allowed(me->data.l[1])) { break; } HSTag* target = get_tag_by_index(desktop_index); client = get_client_from_window(me->window); if (client && target) { tag_move_client(client, target); } break; case NetWmState: client = get_client_from_window(me->window); /* ignore requests for unmanaged windows */ if (!client || !client->ewmhrequests) break; /* mapping between EWMH atoms and client struct members */ struct { int atom_index; bool enabled; void (*callback)(HSClient*, bool); } client_atoms[] = { { NetWmStateFullscreen, client->fullscreen, client_set_fullscreen }, { NetWmStateDemandsAttention, client->urgent, client_set_urgent }, }; /* me->data.l[1] and [2] describe the properties to alter */ for (int prop = 1; prop <= 2; prop++) { if (me->data.l[prop] == 0) { /* skip if no property is specified */ continue; } /* check if we support the property data[prop] */ int i; for (i = 0; i < LENGTH(client_atoms); i++) { if (g_netatom[client_atoms[i].atom_index] == me->data.l[prop]) { break; } } if (i >= LENGTH(client_atoms)) { /* property will not be handled */ continue; } bool new_value[] = { [ _NET_WM_STATE_REMOVE ] = false, [ _NET_WM_STATE_ADD ] = true, [ _NET_WM_STATE_TOGGLE ] = !client_atoms[i].enabled, }; int action = me->data.l[0]; if (action >= LENGTH(new_value)) { HSDebug("_NET_WM_STATE: invalid action %d\n", action); } /* change the value */ client_atoms[i].callback(client, new_value[action]); } break; default: HSDebug("no handler for the client message \"%s\"\n", g_netatom_names[index]); break; } }
void ewmh_handle_client_message(XEvent* event) { HSDebug("Received event: ClientMessage\n"); XClientMessageEvent* me = &(event->xclient); int index; for (index = 0; index < NetCOUNT; index++) { if (me->message_type == g_netatom[index]) { break; } } if (index >= NetCOUNT) { HSDebug("received unknown client message\n"); return; } HSClient* client; int desktop_index; switch (index) { case NetActiveWindow: // only steal focus it allowed to the current source // (i.e. me->data.l[0] in this case as specified by EWMH) if (focus_stealing_allowed(me->data.l[0])) { HSClient* client = get_client_from_window(me->window); if (client) { focus_client(client, true, true); } } break; case NetCurrentDesktop: { desktop_index = me->data.l[0]; if (desktop_index < 0 || desktop_index >= tag_get_count()) { HSDebug("_NET_CURRENT_DESKTOP: invalid index \"%d\"\n", desktop_index); break; } HSTag* tag = get_tag_by_index(desktop_index); monitor_set_tag(get_current_monitor(), tag); break; } case NetWmDesktop: { desktop_index = me->data.l[0]; if (!focus_stealing_allowed(me->data.l[1])) { break; } HSTag* target = get_tag_by_index(desktop_index); client = get_client_from_window(me->window); if (client && target) { tag_move_client(client, target); } break; } case NetWmState: { client = get_client_from_window(me->window); /* ignore requests for unmanaged windows */ if (!client || !client->ewmhrequests) break; /* mapping between EWMH atoms and client struct members */ struct { int atom_index; bool enabled; void (*callback)(HSClient*, bool); } client_atoms[] = { { NetWmStateFullscreen, client->fullscreen, client_set_fullscreen }, { NetWmStateDemandsAttention, client->urgent, client_set_urgent }, }; /* me->data.l[1] and [2] describe the properties to alter */ for (int prop = 1; prop <= 2; prop++) { if (me->data.l[prop] == 0) { /* skip if no property is specified */ continue; } /* check if we support the property data[prop] */ int i; for (i = 0; i < LENGTH(client_atoms); i++) { if (g_netatom[client_atoms[i].atom_index] == me->data.l[prop]) { break; } } if (i >= LENGTH(client_atoms)) { /* property will not be handled */ continue; } auto new_value = ArrayInitializer<bool,3>({ { _NET_WM_STATE_REMOVE , false }, { _NET_WM_STATE_ADD , true }, { _NET_WM_STATE_TOGGLE , !client_atoms[i].enabled }, }).a; int action = me->data.l[0]; if (action >= new_value.size()) { HSDebug("_NET_WM_STATE: invalid action %d\n", action); } /* change the value */ client_atoms[i].callback(client, new_value[action]); } break; } case NetWmMoveresize: { client = get_client_from_window(me->window); if (!client) { break; } int direction = me->data.l[2]; if (direction == _NET_WM_MOVERESIZE_MOVE || direction == _NET_WM_MOVERESIZE_MOVE_KEYBOARD) { mouse_initiate_move(client, 0, NULL); } else if (direction == _NET_WM_MOVERESIZE_CANCEL) { if (mouse_is_dragging()) mouse_stop_drag(); } else { // anything else is a resize mouse_initiate_resize(client, 0, NULL); } break; } default: HSDebug("no handler for the client message \"%s\"\n", g_netatom_names[index]); break; } }