Exemplo n.º 1
0
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);
    }
}
Exemplo n.º 2
0
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]);
    }
}
Exemplo n.º 3
0
Arquivo: main.c Projeto: xiaq/hlwm
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);
    }
}
Exemplo n.º 4
0
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);

}
Exemplo n.º 5
0
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);
    }
}
Exemplo n.º 6
0
Arquivo: ewmh.c Projeto: xiaq/hlwm
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;
    }
}
Exemplo n.º 7
0
Arquivo: main.c Projeto: xiaq/hlwm
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);
}
Exemplo n.º 8
0
Arquivo: main.c Projeto: xiaq/hlwm
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);
        }
    }
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
Arquivo: main.c Projeto: xiaq/hlwm
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
}
Exemplo n.º 11
0
Arquivo: main.c Projeto: xiaq/hlwm
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);
}
Exemplo n.º 12
0
Arquivo: main.c Projeto: xiaq/hlwm
/* 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 */
}
Exemplo n.º 13
0
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);
    }
}
Exemplo n.º 14
0
Arquivo: main.c Projeto: xiaq/hlwm
void keypress(XEvent* event) {
    HSDebug("name is: KeyPress\n");
    handle_key_press(event);
}
Exemplo n.º 15
0
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);
}
Exemplo n.º 16
0
Arquivo: main.c Projeto: xiaq/hlwm
static void handle_signal(int signal) {
    HSDebug("Interrupted by signal %d\n", signal);
    g_aboutToQuit = true;
    return;
}
Exemplo n.º 17
0
Arquivo: main.c Projeto: xiaq/hlwm
void configurerequest(XEvent* event) {
    HSDebug("name is: ConfigureRequest\n");
    event_on_configure(*event);
}
Exemplo n.º 18
0
Arquivo: main.c Projeto: xiaq/hlwm
void buttonrelease(XEvent* event) {
    HSDebug("name is: ButtonRelease\n");
    mouse_stop_drag();
}
Exemplo n.º 19
0
Arquivo: main.c Projeto: xiaq/hlwm
void expose(XEvent* event) {
    HSDebug("name is: Expose\n");
}
Exemplo n.º 20
0
Arquivo: main.c Projeto: xiaq/hlwm
void focusin(XEvent* event) {
    HSDebug("name is: FocusIn\n");
}
Exemplo n.º 21
0
Arquivo: main.c Projeto: xiaq/hlwm
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;
}
Exemplo n.º 22
0
Arquivo: main.c Projeto: xiaq/hlwm
void unmapnotify(XEvent* event) {
    HSDebug("name is: UnmapNotify for %lx\n", event->xunmap.window);
    unmanage_client(event->xunmap.window);
}
Exemplo n.º 23
0
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);
    }
}
Exemplo n.º 24
0
Arquivo: ewmh.c Projeto: xiaq/hlwm
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;
    }
}
Exemplo n.º 25
0
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;
    }
}