Levi Neely

Quit your job! Slack off! The world ends tomorrow, and YOU MAY DIE!

WindowMaker / wlroots

A dual X11 and Wayland implementation of WindowMaker. (This page is a work in progress. Check back later for more info.)

wmaker-wl screenshot

Motivation

Status

The implementation is mostly complete. Currently in the testing and debugging phase, within days or weeks of having a usable "daily driver."

Approach

The original WindowMaker implementation is tightly coupled to X11. The first step was to inspect the code and define all the required functionality in generic (non-X11) terms. This produced a gigantic vtable (code below), to which the X11- and Wayland-specific code must be wired. The same thing was done for WINGs and wrlib.

/*
 * WMBackend -- vtable for all display-system operations.
 *
 * The active backend is pointed to by wm_backend (defined below).
 * Startup code sets wm_backend before any display operation is called.
 */
typedef struct WMBackend {
    int is_wayland;
    void *(*display_open)(const char *name);
    void (*display_close)(void);
    void (*display_post_open)(const char *display_name, int *multi_head_out);
    int (*display_screen_count)(void);
    int (*display_default_screen)(void);
    unsigned long (*atom_intern)(const char *name);
    void (*event_loop_run)(void);
    void (*event_loop_terminate)(void);
    void (*screen_open)(WScreen *scr);
    int (*screen_init_display)(WScreen *scr, int screen_number);
    void (*screen_close)(WScreen *scr);
    WNativeWindow (*frame_create_toplevel)(WScreen *scr,
                                           int x, int y, int width, int height,
                                           int border_width,
                                           int depth, void *visual,
                                           unsigned long colormap,
                                           WMPixel border_pixel);
    WNativeWindow (*frame_create_child)(WNativeWindow parent_win,
                                        WScreen *scr,
                                        int x, int y, int width, int height);
    void (*frame_destroy)(WScreen *scr, WNativeWindow win);
    void (*frame_configure)(WNativeWindow win, int x, int y, int width, int height);
    int (*frame_paint)(struct WFrameWindow *fwin);
    void (*context_save)(WNativeWindow win, WContextKey key, void *data);
    void *(*context_find)(WNativeWindow win, WContextKey key);
    int (*context_delete)(WNativeWindow win, WContextKey key);
    void (*focus_set)(WNativeWindow win, int revert_to, unsigned long timestamp);
    void (*key_grab)(WNativeWindow win, unsigned int keycode,
                     unsigned int modifier, int owner_events);
    void (*key_ungrab_all)(WNativeWindow win);
    void (*button_grab)(WNativeWindow win, unsigned int button,
                        unsigned int modifier, unsigned int event_mask,
                        int owner_events);
    void (*button_ungrab_all)(WNativeWindow win);
    int (*pointer_grab)(WNativeWindow grab_win, WNativeWindow confine_to,
                        unsigned int event_mask, int cursor_id,
                        int owner_events, unsigned long timestamp);
    void (*pointer_ungrab)(unsigned long timestamp);
    void (*pointer_warp)(int dx, int dy);
    int (*pointer_query)(WNativeWindow root, int *root_x, int *root_y);
    void (*client_synth_configure)(WNativeWindow client_win,
                                    int x, int y, int width, int height,
                                    int border_width, WNativeWindow above);
    void (*stacking_restack)(WNativeWindow *windows, int count);
    void (*stacking_raise)(WNativeWindow win);
    void (*stacking_lower)(WNativeWindow win);
    int (*stacking_query)(WNativeWindow root,
                          WNativeWindow **out, unsigned int *count);
    void (*stacking_set_level)(WNativeWindow win, int level);
    void (*event_flush)(void);
    void (*event_sync)(int discard);
    int (*event_pending)(void);
    int (*prop_get_normal_hints)(WNativeWindow win, WNormalHints *out,
                                 int *pre_icccm);
    WWMHints *(*prop_get_wm_hints)(WNativeWindow win);
    int (*prop_get_wm_class)(WNativeWindow win,
                             char **wm_class, char **wm_instance);
    Bool (*window_fetch_name)(WNativeWindow win, char **name_out);
    Bool (*window_get_icon_name)(WNativeWindow win, char **name_out);
    Bool (*screen_get_head_usable_area)(WScreen *scr, int head,
                                        int *x1, int *y1, int *x2, int *y2);
    void *(*window_get_icon_image)(WNativeWindow win);
    int (*prop_get_wmaker_menu)(WNativeWindow win,
                                char ***list_out, int *count_out);
    void (*window_map)(WNativeWindow win);
    void (*window_map_raised)(WNativeWindow win);
    void (*window_unmap)(WNativeWindow win);
    void (*window_move)(WNativeWindow win, int x, int y);
    void (*window_resize)(WNativeWindow win, int width, int height);
    int (*pointer_query_full)(WNativeWindow root,
                              WNativeWindow *child_out,
                              int *root_x, int *root_y,
                              unsigned int *buttons_out);
    RImage *(*workspace_badge_render)(WScreen *scr, WNativeWindow badge_win,
                                      const char *name, int name_len,
                                      int width, int height);
    void (*workspace_badge_update)(WScreen *scr, WNativeWindow badge_win,
                                   RImage *img);
    RImage *(*workspace_badge_get_background)(WScreen *scr, int x, int y,
                                              int width, int height);
    void (*server_grab)(void);
    void (*server_ungrab)(void);
    void (*overlay_draw_lines)(WScreen *scr, WPoint *points, int n);
    void (*overlay_draw_rect)(WScreen *scr, int x, int y, int w, int h);
    int (*window_get_rect)(WNativeWindow win,
                           int *win_x, int *win_y, int *win_w, int *win_h);
    RImage *(*client_snapshot)(WScreen *scr,
                               WNativeWindow client_win,
                               WNativeWindow frame_win);
    void (*refresh_desktop)(WScreen *scr);
    void (*set_desktop_background)(WScreen *scr, const char *texture_desc);
    void (*window_select_events)(WNativeWindow win, unsigned long mask);
    void (*window_set_border)(WNativeWindow win, unsigned long pixel);
    void (*window_set_border_width)(WNativeWindow win, int width);
    void (*pointer_warp_to)(int x, int y);
    void (*event_flush_expose)(void);
    int (*keyboard_grab)(WNativeWindow win);
    void (*keyboard_ungrab)(void);
    unsigned int (*keysym_to_keycode)(unsigned long keysym);
    void (*pointer_change_grab)(unsigned int event_mask, int cursor_id,
                                unsigned long timestamp);
    void (*event_drain_motion)(void);
    void (*event_discard_enter_leave_until)(unsigned long timestamp);
    int (*coords_translate)(WNativeWindow src, WNativeWindow dst,
                            int src_x, int src_y, int *dst_x, int *dst_y);
    void (*window_set_save_under)(WNativeWindow win, int save_under);
    void (*window_set_background)(WNativeWindow win, unsigned long pixel);
    void (*window_set_background_pixmap)(WNativeWindow win, unsigned long pixmap);
    unsigned long (*keysym_from_keycode)(unsigned int keycode, unsigned int index);
    void (*overlay_draw_line)(WScreen *scr, int x1, int y1, int x2, int y2);
    void (*overlay_draw_segments)(WScreen *scr, WSegment *segs, int n);
    int (*overlay_text_width)(WScreen *scr, const char *str, int n);
    void (*overlay_draw_string)(WScreen *scr, int x, int y,
                                const char *str, int n);
    void (*window_clear)(WNativeWindow win);
    void (*window_map_subwindows)(WNativeWindow parent);
    void (*pixmap_free)(unsigned long pixmap);
    int (*window_get_client_attrs)(WNativeWindow win, WClientAttributes *out);
    void (*client_get_visual_attrs)(WNativeWindow win, WClientVisual *out);
    void (*window_add_events)(WNativeWindow win, unsigned long extra_mask);
    void (*client_adopt_attributes)(WNativeWindow win, unsigned long event_mask);
    void (*client_reparent)(WNativeWindow client, WNativeWindow new_parent,
                            int x, int y);
    void (*client_add_to_save_set)(WNativeWindow win);
    void (*client_remove_from_save_set)(WNativeWindow win);
    void (*client_close)(WNativeWindow win);
    WNativeWindow (*fake_leader_create)(WScreen *scr);
    void (*fake_leader_destroy)(WNativeWindow win);
    void (*icccm_set_class_hint)(WNativeWindow win,
                                 const char *instance, const char *class_name);
    void (*icccm_set_class_hint_if_absent)(WNativeWindow win,
                                           const char *instance,
                                           const char *class_name);
    void (*icccm_set_wm_hints)(WNativeWindow win, const WWMHints *hints);
    void (*icccm_set_command)(WNativeWindow win, char **argv, int argc);
    int (*icccm_get_command)(WNativeWindow win, char ***argv_out, int *argc_out);
    void (*icccm_free_command)(char **argv);
    WNativeWindow (*prop_get_transient_for)(WNativeWindow win);
    int (*prop_has_wmaker_menu)(WNativeWindow win);
    int (*prop_has_gtk_object_path)(WNativeWindow win);
    void (*prop_set_wm_state)(WNativeWindow win, const long *data, int n);
    int (*prop_get_wm_state)(WNativeWindow win, long **data_out);
    int (*client_get_pid)(WNativeWindow win);
    void (*window_shape_clear)(WWindow *wwin);
    void (*window_shape_set)(WWindow *wwin);
    void (*client_query_shaped)(WNativeWindow win, int *shaped_out);
    int (*client_get_gnustep_attrs)(WNativeWindow win, GNUstepWMAttributes **attr_out);
    void (*monitors_query)(WScreen *scr,
                           WHeadGeometry **heads_out, int *count_out);
    void (*monitors_select_events)(WScreen *scr);
    int (*screen_replace_wm)(WScreen *scr);
    int (*screen_select_input)(WScreen *scr, long event_mask);
    void (*screen_set_root_cursor)(WScreen *scr, int cursor_id);
    void (*screen_set_icon_sizes)(WScreen *scr, int min_size, int max_size);
    void (*screen_setup_noticeboard)(WScreen *scr);
    void (*screen_alloc_gcs)(WScreen *scr);
    void (*screen_create_internals)(WScreen *scr);
    void (*screen_create_pixmaps)(WScreen *scr);
    void (*screen_load_tech_font)(WScreen *scr);
    RImage *(*screen_capture_area)(WScreen *scr);
    RImage *(*screen_capture_window)(WScreen *scr, WWindow *wwin);
    void (*screen_show_mini_screenshot)(WScreen *scr, RImage *img);
    void (*event_handle_extensions)(WMEvent *event);
    void (*event_randr_update)(WMEvent *event);
    int (*xkb_get_group)(void);
    void (*xkb_lock_group)(int group);
    void (*register_error_handlers)(void);
    void (*extensions_init)(void);
    void (*cursors_init)(void);
    WNativeWindow (*cursor_create_named)(const char *name);
    void (*shape_combine_mask)(WNativeWindow win, WNativeWindow mask);
    void (*texture_alloc_color)(WScreen *scr, WMColorRGB *color);
    void (*texture_free_colors)(WScreen *scr, unsigned long *pixels, int count);
    GC (*texture_create_gc)(WScreen *scr, unsigned long pixel);
    void (*texture_free_gc)(GC gc);
    void (*frame_alloc_border_pixel)(unsigned long colormap,
                                     const char *color_name,
                                     unsigned long **pixel_out);
    void (*frame_update_texture)(struct WFrameWindow *fwin);
    void (*frame_paint_button)(WCoreWindow *button, WTexture *texture,
                               unsigned long color, WPixmap *image,
                               int pushed);
    void (*netwm_init)(WScreen *scr);
    void (*netwm_cleanup)(WScreen *scr);
    void (*netwm_update_desktop)(WScreen *scr);
    int (*netwm_get_current_desktop)(WScreen *scr);
    void (*netwm_update_opacity)(WWindow *wwin);
    void (*netwm_update_actions)(WWindow *wwin, int del);
    void (*netwm_check_client_hints)(WWindow *wwin, int *layer, int *workspace);
    void (*netwm_check_initial_state)(WWindow *wwin);
    int (*netwm_get_pid)(WNativeWindow window);
    void (*netwm_cleanup_frame_extents)(WWindow *wwin);
    void (*netwm_set_frame_extents)(WWindow *wwin);
    void (*prop_get_protocols)(WNativeWindow window, WProtocols *prots);
    unsigned char *(*prop_get_check_property)(WNativeWindow window,
                                              unsigned long hint,
                                              unsigned long type,
                                              int format, int count,
                                              int *ret_count);
    void (*prop_set_wmaker_protocols)(WNativeWindow root);
    void (*prop_set_icon_tile)(WScreen *scr, RImage *image);
    void (*prop_cleanup)(WNativeWindow root);
    void (*client_restore)(WWindow *wwin);
    void (*client_set_state)(WWindow *wwin, int state, WNativeWindow icon_win);
    void (*client_set_maximized)(WNativeWindow win, int maximized);
    void (*client_send_protocol)(WWindow *wwin, unsigned long protocol,
                                 unsigned long timestamp);
    void (*client_kill)(WWindow *wwin);
    void (*client_get_colormap_windows)(WWindow *wwin);
    void (*eat_expose)(void);
    void (*move_window)(WNativeWindow win, int from_x, int from_y,
                        int to_x, int to_y);
    void (*slide_windows)(WNativeWindow *wins, int n,
                          int from_x, int from_y, int to_x, int to_y);
    char *(*get_text_selection)(WScreen *scr, unsigned long selection);
    char *(*get_shortcut_string)(WShortKey key);
    char *(*get_command_for_window)(WNativeWindow win, int elements);
    void (*icon_update_pixmap)(WIcon *icon);
    void (*icon_paint)(WIcon *icon);
    void (*appicon_show)(WApplication *wapp);
    void (*appicon_paint)(WAppIcon *aicon);
    void (*restore_desktop)(WScreen *scr);
    void (*wipe_desktop)(WScreen *scr);
    int (*get_color_for_colormap)(unsigned long colormap,
                                  const char *color_name, WMColorRGB *color);
    void (*free_color)(WScreen *scr, unsigned long pixel);
    void (*colormap_install)(WScreen *scr, WWindow *wwin);
    void (*xdnd_init)(void);
    int (*motif_get_hints)(WNativeWindow window, MWMHints *hints_out);
    void (*appicon_bounce)(WAppIcon *aicon, void *data);
    void (*manage_all_windows)(WScreen *scr, int crash_recovery);
    void (*event_handle_signal_exit)(void);
    void (*icon_select)(WIcon *icon);
    void (*icon_change_title)(WIcon *icon, WWindow *wwin);
    void (*clip_icon_paint)(WAppIcon *aicon);
    void (*drawer_icon_paint)(WAppIcon *aicon);
    void (*balloon_init)(WScreen *scr);
    void (*do_kaboom)(WScreen *scr, WNativeWindow win, int x, int y);
    void (*menu_paint_entry)(WMenu *menu, int index, int selected);
    void (*menu_draw_frame)(WScreen *scr, WNativeWindow win,
                            int y, int w, int h, int type);
    void (*draw_bevel)(WNativeWindow d, unsigned int width, unsigned int height,
                       WTexSolid *texture, int relief);
    void (*appmenu_send)(WNativeWindow window, int what, int tag);
    const char *(*display_get_name)(const char *name);
    void (*switchpanel_send_enter)(WNativeWindow info_win, WNativeWindow window);
    void (*format_x_error)(void *error, char *buf, int size);
    WPixmap *(*pixmap_create_from_xbm)(WScreen *scr, char *data, char *mask,
                                        int width, int height,
                                        unsigned long fg, unsigned long bg);
    WPixmap *(*pixmap_create)(unsigned long image, unsigned long mask);
    unsigned long (*keysym_from_name)(const char *name);
    void (*frame_paint_resizebar)(struct WFrameWindow *fwin);
    void (*frame_paint_titlebar)(struct WFrameWindow *fwin, int state);
    WNativeWindow (*prop_get_client_leader)(WNativeWindow window);
    int (*prop_get_window_state)(WNativeWindow window);
    void (*setup_modifier_lock_masks)(void);
    void (*key_grab_hacked)(WNativeWindow window, int keycode, unsigned int modifiers);
    void (*button_grab_hacked)(unsigned int button, unsigned int modifiers,
                               WNativeWindow window, int owner_events,
                               unsigned int event_mask);
    void (*context_init)(void);
    void (*query_tree)(WNativeWindow root, WNativeWindow **children_out,
                       unsigned int *nchildren_out);
    WScreen *(*screen_for_window)(WNativeWindow window);
    int (*parse_color)(WScreen *scr, const char *name, WMColorRGB *color_out);
    int (*cursor_create_from_files)(WScreen *scr, const char *bitmap_path,
                                    const char *mask_path, WMNativeCursor *cursor_out);
    void (*cursor_free)(WMNativeCursor cursor);
    void (*cursor_set)(WNativeWindow window, WMNativeCursor cursor);
    void (*pointer_change_cursor)(int cursor_id);
    void (*appicon_paint_dock_dots)(struct WAppIcon *aicon, void *wapp);
    int (*pixmap_get_size)(unsigned long drawable, unsigned int *w, unsigned int *h, unsigned int *depth);
    void (*icon_paint_title_bg)(WScreen *scr, unsigned long pixmap, int height);
    void (*icon_select_paint)(WIcon *icon, int step);
    unsigned long (*window_get_event_mask)(WNativeWindow window);
    void (*balloon_show)(WScreen *scr, void *bal);
    void (*window_fill_stipple)(WNativeWindow win, int x, int y, int w, int h);
    int (*event_check_enter_leave)(void *dock);
    void (*event_allow_async)(void);
    void (*event_replay_pointer)(void);
    void (*keyboard_mapping_refresh)(WMEvent *event);
    void (*event_flush_window)(WNativeWindow win, int event_type);
    void (*event_flush_type)(int event_type);
    int (*event_check_window)(WNativeWindow win, int event_type);
    int (*event_check_type)(int event_type, WMEvent *event_out);
    void (*client_send_event)(WNativeWindow win, void *event_data);
    void (*client_message_forward)(WMEvent *event, WNativeWindow target_win);
    void (*client_send_key)(WNativeWindow win, unsigned int keycode,
                            unsigned int modifier_mask, int press);
    int (*window_exists)(WNativeWindow win);
    void (*window_get_colormap)(WNativeWindow win, unsigned long *colormap_out);
    WNativeWindow (*pointer_query_window)(WNativeWindow root);
    void (*dialog_paint_icon)(unsigned long drawable, void *back_color,
                              void *pixmap, WScreen *scr,
                              int x, int y, int width, int height);
    void (*pointer_get_position)(int *x, int *y);
    const char *(*keysym_to_string)(unsigned long keysym);
    int (*window_get_command)(WNativeWindow win, char ***argv_out, int *argc_out);
    int (*netwm_process_client_message)(WMEvent *event);
    int (*xdnd_process_client_message)(WMEvent *event);
    void (*xdnd_process_selection)(WMEvent *event);
    void (*xdnd_make_awareness)(WNativeWindow win);
    void (*netwm_check_client_hint_change)(WWindow *wwin, void *event);
    void (*handle_selection_request)(WMEvent *event);
    void (*handle_selection_clear)(WMEvent *event);
    void (*netwm_position_splash)(WWindow *wwin, int *x, int *y, int width, int height);
    void (*netwm_check_initial_frame_state)(WWindow *wwin);
    RContext *(*create_rcontext)(int screen_number, RContextAttributes *attribs);
    int (*show_crash_dialog)(int sig);
} WMBackend;
		

After the WindowMaker, WINGs, and wrlib vtables were defined, the X11 implementations were extracted from each and replaced with the vtable calls. At the same time, the X11 implementation was built out and plugged in to the vtable. Finally, when a working X11 implementation was in place, noop stubs were created for Wayland. The Wayland stubs were then implemented piecemeal.

Challenges

Training. One of the most consistent difficulties in using coding agents for this project, is that their training and prompting drives them to make minimal changes to the code they are working with. This works well in most cases, but in this project — where correctness was critical — the agent had to be repeatedly reminded to adhere strictly to the architecture, and not cut corners with backend-specific guards in the core window manager / libraries.

Cost. The cost of using "frontier" coding agents is quite high. This project started with Kiro and Claude Code subscription models. Limits were rapidly reached for both subscriptions (hours or days, not weeks). I found, in the later stages of this project, that using a small "team" of agents — GPT 5.4 for planning / review and DeepSeek v4 Flash for coding and debugging — was both cost-efficient and effective for most tasks. For especially difficult problems, Claude Sonnet or Opus could be added to the team on demand. With ollie, these agents could communicate directly with each other to share knowledge / context. A shared "memory" repository enabled agents to persist knowledge across multiple sessions. The price of each turn was reduced by 90-99%, and the amount of code exploration needed was reduced significantly.

Concurrency. Because this implementation is completely different than the upstream, it would be next to impossible to remain up to date. Therefore, the latest stable release (0.96.0) was chosen as a basis. If practical, the updates will be backported until this implementation is current with upstream.

Back