JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Add new maximize_toggle action.
[spectrwm.git] / spectrwm.c
index e202f9c..acf078a 100644 (file)
@@ -285,8 +285,8 @@ xcb_atom_t          a_takefocus;
 xcb_atom_t             a_utf8_string;
 xcb_atom_t             a_swm_iconic;
 xcb_atom_t             a_swm_ws;
-volatile sig_atomic_t  running = 1;
-volatile sig_atomic_t  restart_wm = 0;
+volatile sig_atomic_t   running = 1;
+volatile sig_atomic_t   restart_wm = 0;
 xcb_timestamp_t                last_event_time = 0;
 int                    outputs = 0;
 int                    other_wm;
@@ -345,10 +345,10 @@ double                    dialog_ratio = 0.6;
 #define SWM_BAR_JUSTIFY_CENTER (1)
 #define SWM_BAR_JUSTIFY_RIGHT  (2)
 #define SWM_BAR_OFFSET         (4)
-#define SWM_BAR_FONTS          "-*-terminus-medium-*-*-*-12-*-*-*-*-*-*-*,"\
-                               "-*-profont-*-*-*-*-12-*-*-*-*-*-*-*,"\
-                               "-*-times-medium-r-*-*-12-*-*-*-*-*-*-*,"\
-                               "-misc-fixed-medium-r-*-*-12-*-*-*-*-*-*-*,"\
+#define SWM_BAR_FONTS          "-*-terminus-medium-*-*-*-12-*-*-*-*-*-*-*," \
+                               "-*-profont-*-*-*-*-12-*-*-*-*-*-*-*,"      \
+                               "-*-times-medium-r-*-*-12-*-*-*-*-*-*-*,"    \
+                               "-misc-fixed-medium-r-*-*-12-*-*-*-*-*-*-*,"  \
                                "-*-*-*-r-*-*-*-*-*-*-*-*-*-*"
 
 #ifdef X_HAVE_UTF8_STRING
@@ -438,11 +438,11 @@ struct ws_win {
        struct swm_geometry     g;              /* current geometry */
        struct swm_geometry     g_float;        /* region coordinates */
        int                     g_floatvalid;   /* g_float geometry validity */
-       int                     floatmaxed;     /* whether maxed by max_stack */
        int                     floating;
        int                     manual;
        int32_t                 mapped;
        int32_t                 iconic;
+       int32_t                 maximized;
        int                     bordered;
        unsigned int            ewmh_flags;
        int                     font_size_boundary[SWM_MAX_FONT_STEPS];
@@ -517,6 +517,7 @@ struct workspace {
        struct swm_region       *old_r;         /* may be NULL */
        struct ws_win_list      winlist;        /* list of windows in ws */
        struct ws_win_list      unmanagedlist;  /* list of dead windows in ws */
+       int32_t                 state;
        char                    stacker[10];    /* display stacker and layout */
 
        /* stacker state */
@@ -533,6 +534,12 @@ struct workspace {
 };
 
 enum {
+       SWM_WS_STATE_HIDDEN,
+       SWM_WS_STATE_MAPPING,
+       SWM_WS_STATE_MAPPED,
+};
+
+enum {
        SWM_S_COLOR_BAR,
        SWM_S_COLOR_BAR_BORDER,
        SWM_S_COLOR_BAR_BORDER_UNFOCUS,
@@ -672,33 +679,33 @@ struct ewmh_hint {
        char            *name;
        xcb_atom_t      atom;
 } ewmh[SWM_EWMH_HINT_MAX] =    {
-       /* must be in same order as in the enum */
-       {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE},
-       {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE},
-       {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE},
-       {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE},
-       {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE},
-       {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE},
-       {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE},
-       {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE},
-       {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE},
-       {"_NET_WM_NAME", XCB_ATOM_NONE},
-       {"_NET_WM_STATE", XCB_ATOM_NONE},
-       {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE},
-       {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE},
-       {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE},
-       {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE},
-       {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE},
-       {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE},
-       {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE},
-       {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE},
-       {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE},
-       {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE},
-       {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE},
-       {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE},
-       {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE},
-       {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE},
-       {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE},
+    /* must be in same order as in the enum */
+    {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE},
+    {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE},
+    {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE},
+    {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE},
+    {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE},
+    {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE},
+    {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE},
+    {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE},
+    {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE},
+    {"_NET_WM_NAME", XCB_ATOM_NONE},
+    {"_NET_WM_STATE", XCB_ATOM_NONE},
+    {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE},
+    {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE},
+    {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE},
+    {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE},
+    {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE},
+    {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE},
+    {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE},
+    {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE},
 };
 
 /* Cursors */
@@ -752,6 +759,7 @@ enum keyfuncid {
        KF_FOCUS_NEXT,
        KF_FOCUS_PREV,
        KF_FOCUS_URGENT,
+       KF_MAXIMIZE_TOGGLE,
        KF_HEIGHT_GROW,
        KF_HEIGHT_SHRINK,
        KF_ICONIFY,
@@ -860,237 +868,247 @@ enum keyfuncid {
 };
 
 struct key {
-       RB_ENTRY(key)   entry;
-       unsigned int    mod;
-       KeySym          keysym;
-       enum keyfuncid  funcid;
-       char            *spawn_name;
+        RB_ENTRY(key)           entry;
+        unsigned int            mod;
+        KeySym                  keysym;
+        enum keyfuncid          funcid;
+        char                    *spawn_name;
 };
 RB_HEAD(key_tree, key);
 
 /* function prototypes */
-void   adjust_font(struct ws_win *);
+void    adjust_font(struct ws_win *);
 char   *argsep(char **);
-void   bar_cleanup(struct swm_region *);
-void   bar_extra_setup(void);
-void   bar_extra_stop(void);
-int    bar_extra_update(void);
-void   bar_fmt(const char *, char *, struct swm_region *, size_t);
-void   bar_fmt_expand(char *, size_t);
-void   bar_draw(void);
-void   bar_print(struct swm_region *, const char *);
-void   bar_print_legacy(struct swm_region *, const char *);
-void   bar_replace(char *, char *, struct swm_region *, size_t);
-void   bar_replace_pad(char *, int *, size_t);
+void    bar_cleanup(struct swm_region *);
+void    bar_extra_setup(void);
+void    bar_extra_stop(void);
+int     bar_extra_update(void);
+void    bar_fmt(const char *, char *, struct swm_region *, size_t);
+void    bar_fmt_expand(char *, size_t);
+void    bar_draw(void);
+void    bar_print(struct swm_region *, const char *);
+void    bar_print_legacy(struct swm_region *, const char *);
+void    bar_replace(char *, char *, struct swm_region *, size_t);
+void    bar_replace_pad(char *, int *, size_t);
 char   *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t);
-void   bar_setup(struct swm_region *);
-void   bar_toggle(struct swm_region *, union arg *);
-void   bar_urgent(char *, size_t);
-void   bar_window_class(char *, size_t, struct swm_region *);
-void   bar_window_class_instance(char *, size_t, struct swm_region *);
-void   bar_window_float(char *, size_t, struct swm_region *);
-void   bar_window_instance(char *, size_t, struct swm_region *);
-void   bar_window_name(char *, size_t, struct swm_region *);
-void   bar_workspace_name(char *, size_t, struct swm_region *);
-void   buttonpress(xcb_button_press_event_t *);
-void   check_conn(void);
-void   clear_keys(void);
-void   clientmessage(xcb_client_message_event_t *);
-void   client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t);
-int    conf_load(const char *, int);
-void   configurenotify(xcb_configure_notify_event_t *);
-void   configurerequest(xcb_configure_request_event_t *);
-void   config_win(struct ws_win *, xcb_configure_request_event_t *);
-void   constrain_window(struct ws_win *, struct swm_geometry *, int *);
-int    count_win(struct workspace *, int);
-void   cursors_cleanup(void);
-void   cursors_load(void);
-void   custom_region(const char *);
-void   cyclerg(struct swm_region *, union arg *);
-void   cyclews(struct swm_region *, union arg *);
-void   cycle_layout(struct swm_region *, union arg *);
-void   destroynotify(xcb_destroy_notify_event_t *);
-void   dumpwins(struct swm_region *, union arg *);
-int    enable_wm(void);
-void   enternotify(xcb_enter_notify_event_t *);
-void   event_drain(uint8_t);
-void   event_error(xcb_generic_error_t *);
-void   event_handle(xcb_generic_event_t *);
-void   ewmh_autoquirk(struct ws_win *);
-void   ewmh_get_win_state(struct ws_win *);
-int    ewmh_set_win_fullscreen(struct ws_win *, int);
-void   ewmh_update_actions(struct ws_win *);
-void   ewmh_update_win_state(struct ws_win *, xcb_atom_t, long);
+void    bar_setup(struct swm_region *);
+void    bar_toggle(struct swm_region *, union arg *);
+void    bar_urgent(char *, size_t);
+void    bar_window_class(char *, size_t, struct swm_region *);
+void    bar_window_class_instance(char *, size_t, struct swm_region *);
+void    bar_window_float(char *, size_t, struct swm_region *);
+void    bar_window_instance(char *, size_t, struct swm_region *);
+void    bar_window_name(char *, size_t, struct swm_region *);
+void    bar_window_state(char *, size_t, struct swm_region *);
+void    bar_workspace_name(char *, size_t, struct swm_region *);
+void    buttonpress(xcb_button_press_event_t *);
+void    check_conn(void);
+void    clear_keys(void);
+int     clear_maximized(struct workspace *);
+void    clientmessage(xcb_client_message_event_t *);
+void    client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t);
+int     conf_load(const char *, int);
+void    configurenotify(xcb_configure_notify_event_t *);
+void    configurerequest(xcb_configure_request_event_t *);
+void    config_win(struct ws_win *, xcb_configure_request_event_t *);
+void    constrain_window(struct ws_win *, struct swm_geometry *, int *);
+int     count_win(struct workspace *, int);
+void    cursors_cleanup(void);
+void    cursors_load(void);
+void    custom_region(const char *);
+void    cyclerg(struct swm_region *, union arg *);
+void    cyclews(struct swm_region *, union arg *);
+void    cycle_layout(struct swm_region *, union arg *);
+void    destroynotify(xcb_destroy_notify_event_t *);
+void    dumpwins(struct swm_region *, union arg *);
+int     enable_wm(void);
+void    enternotify(xcb_enter_notify_event_t *);
+void    event_drain(uint8_t);
+void    event_error(xcb_generic_error_t *);
+void    event_handle(xcb_generic_event_t *);
+void    ewmh_autoquirk(struct ws_win *);
+void    ewmh_get_win_state(struct ws_win *);
+int     ewmh_set_win_fullscreen(struct ws_win *, int);
+void    ewmh_update_actions(struct ws_win *);
+void    ewmh_update_win_state(struct ws_win *, xcb_atom_t, long);
 char   *expand_tilde(const char *);
-void   expose(xcb_expose_event_t *);
-void   fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t);
-void   floating_toggle(struct swm_region *, union arg *);
-int    floating_toggle_win(struct ws_win *);
-void   focus(struct swm_region *, union arg *);
-void   focus_flush(void);
-void   focus_region(struct swm_region *);
-void   focusrg(struct swm_region *, union arg *);
-void   focus_win(struct ws_win *);
-void   fontset_init(void);
-void   free_window(struct ws_win *);
-void   search_do_resp(void);
-void   search_resp_name_workspace(const char *, size_t);
-void   search_resp_search_window(const char *);
-void   search_resp_search_workspace(const char *);
-void   search_resp_uniconify(const char *, size_t);
-void   search_win(struct swm_region *, union arg *);
-void   search_win_cleanup(void);
-void   search_workspace(struct swm_region *, union arg *);
-void   send_to_rg(struct swm_region *, union arg *);
-void   send_to_ws(struct swm_region *, union arg *);
-void   set_region(struct swm_region *);
-int    setautorun(const char *, const char *, int);
-int    setconfbinding(const char *, const char *, int);
-int    setconfcolor(const char *, const char *, int);
-int    setconfmodkey(const char *, const char *, int);
-int    setconfquirk(const char *, const char *, int);
-int    setconfregion(const char *, const char *, int);
-int    setconfspawn(const char *, const char *, int);
-int    setconfvalue(const char *, const char *, int);
-void   setkeybinding(unsigned int, KeySym, enum keyfuncid, const char *);
-int    setkeymapping(const char *, const char *, int);
-int    setlayout(const char *, const char *, int);
-void   setquirk(const char *, const char *, const char *,unsigned long);
-void   setscreencolor(const char *, int, int);
-void   setspawn(const char *, const char *, int);
-void   setup_ewmh(void);
-void   setup_globals(void);
-void   setup_keys(void);
-void   setup_quirks(void);
-void   setup_screens(void);
-void   setup_spawn(void);
-void   set_child_transient(struct ws_win *, xcb_window_t *);
-void   set_swm_iconic(struct ws_win *, int);
-void   set_win_state(struct ws_win *, uint16_t);
-void   shutdown_cleanup(void);
-void   sighdlr(int);
-void   socket_setnonblock(int);
-void   sort_windows(struct ws_win_list *);
-void   spawn(int, union arg *, int);
-void   spawn_custom(struct swm_region *, union arg *, const char *);
-int    spawn_expand(struct swm_region *, union arg *, const char *, char ***);
-void   spawn_insert(const char *, const char *, int);
-int32_t        get_swm_iconic(struct ws_win *);
-char   *get_win_name(xcb_window_t);
-void   get_wm_protocols(struct ws_win *);
-int    get_ws_idx(xcb_window_t);
-void   grabbuttons(struct ws_win *);
-void   grabkeys(void);
-void   grab_windows(void);
-void   iconify(struct swm_region *, union arg *);
-int    isxlfd(char *);
-void   keypress(xcb_key_press_event_t *);
-int    key_cmp(struct key *, struct key *);
-void   key_insert(unsigned int, KeySym, enum keyfuncid, const char *);
-void   key_remove(struct key *);
-void   key_replace(struct key *, unsigned int, KeySym, enum keyfuncid,
-           const char *);
-void   kill_bar_extra_atexit(void);
-void   kill_refs(struct ws_win *);
-void   load_float_geom(struct ws_win *);
-void   map_window(struct ws_win *, xcb_window_t);
-void   mapnotify(xcb_map_notify_event_t *);
-void   mappingnotify(xcb_mapping_notify_event_t *);
-void   maprequest(xcb_map_request_event_t *);
-void   motionnotify(xcb_motion_notify_event_t *);
-void   move(struct ws_win *, union arg *);
-void   move_step(struct swm_region *, union arg *);
-uint32_t name_to_pixel(int, const char *);
-void   name_workspace(struct swm_region *, union arg *);
-void   new_region(struct swm_screen *, int, int, int, int);
-int    parsekeys(const char *, unsigned int, unsigned int *, KeySym *);
-int    parsequirks(const char *, unsigned long *);
-int    parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *);
-void   pressbutton(struct swm_region *, union arg *);
-void   priorws(struct swm_region *, union arg *);
-void   propertynotify(xcb_property_notify_event_t *);
-void   quirk_free(struct quirk *);
-void   quirk_insert(const char *, const char *, const char *,unsigned long);
-void   quirk_remove(struct quirk *);
-void   quirk_replace(struct quirk *, const char *, const char *, const char *,
-           unsigned long);
-void   quit(struct swm_region *, union arg *);
-void   raise_toggle(struct swm_region *, union arg *);
-void   region_containment(struct ws_win *, struct swm_region *, int);
-void   regionize(struct ws_win *, int, int);
-void   resize(struct ws_win *, union arg *);
-void   resize_step(struct swm_region *, union arg *);
-void   restart(struct swm_region *, union arg *);
-void   screenchange(xcb_randr_screen_change_notify_event_t *);
-void   scan_xrandr(int);
-void   spawn_remove(struct spawn_prog *);
-void   spawn_replace(struct spawn_prog *, const char *, const char *, int);
-void   spawn_select(struct swm_region *, union arg *, const char *, int *);
-void   stack_config(struct swm_region *, union arg *);
-void   stack_floater(struct ws_win *, struct swm_region *);
-void   stack_master(struct workspace *, struct swm_geometry *, int, int);
-void   store_float_geom(struct ws_win *);
-char   *strdupsafe(const char *);
-void   swapwin(struct swm_region *, union arg *);
-void   switchws(struct swm_region *, union arg *);
-void   teardown_ewmh(void);
-void   unescape_selector(char *);
-void   unfocus_win(struct ws_win *);
-void   uniconify(struct swm_region *, union arg *);
-void   unmanage_window(struct ws_win *);
-void   unmapnotify(xcb_unmap_notify_event_t *);
-void   unmap_all(void);
-void   unmap_window(struct ws_win *);
-void   updatenumlockmask(void);
-void   update_modkey(unsigned int);
-void   update_window(struct ws_win *);
-void   validate_spawns(void);
-int    validate_win(struct ws_win *);
-int    validate_ws(struct workspace *);
-void   version(struct swm_region *, union arg *);
-void   win_to_ws(struct ws_win *, int, int);
-pid_t  window_get_pid(xcb_window_t);
-void   wkill(struct swm_region *, union arg *);
-void   workaround(void);
-void   xft_init(struct swm_region *);
-void   _add_startup_exception(const char *, va_list);
-void   add_startup_exception(const char *, ...);
-#ifdef SWM_DEBUG
-void   focusin(xcb_focus_in_event_t *);
-void   focusout(xcb_focus_out_event_t *);
-void   leavenotify(xcb_leave_notify_event_t *);
-void   print_win_geom(xcb_window_t);
-#endif
-
+void    expose(xcb_expose_event_t *);
+void    fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t);
 struct pid_e   *find_pid(pid_t);
 struct ws_win  *find_unmanaged_window(xcb_window_t);
 struct ws_win  *find_window(xcb_window_t);
-struct ws_win  *get_focus_magic(struct ws_win *);
-struct ws_win  *get_focus_prev(struct ws_win *);
+void    floating_toggle(struct swm_region *, union arg *);
+int     floating_toggle_win(struct ws_win *);
+void    focus(struct swm_region *, union arg *);
+#ifdef SWM_DEBUG
+void    focusin(xcb_focus_in_event_t *);
+void    focusout(xcb_focus_out_event_t *);
+#endif
+void    focus_flush(void);
+void    focus_region(struct swm_region *);
+void    focusrg(struct swm_region *, union arg *);
+void    focus_win(struct ws_win *);
+void    fontset_init(void);
+void    free_window(struct ws_win *);
+xcb_atom_t get_atom_from_string(const char *);
+#ifdef SWM_DEBUG
+char   *get_atom_name(xcb_atom_t);
+#endif
+struct ws_win   *get_focus_magic(struct ws_win *);
+struct ws_win   *get_focus_prev(struct ws_win *);
+#ifdef SWM_DEBUG
+char   *get_notify_detail_label(uint8_t);
+char   *get_notify_mode_label(uint8_t);
+#endif
 struct ws_win  *get_pointer_win(xcb_window_t);
 struct ws_win  *get_region_focus(struct swm_region *);
-struct ws_win  *manage_window(xcb_window_t, uint16_t);
-int            get_region_index(struct swm_region *);
-xcb_atom_t     get_atom_from_string(const char *);
+int     get_region_index(struct swm_region *);
 xcb_screen_t   *get_screen(int);
-xcb_window_t   get_sibling(struct ws_win *, int);
-int            get_screen_count(void);
-uint32_t       getstate(xcb_window_t);
+xcb_window_t    get_sibling(struct ws_win *, int);
+int     get_screen_count(void);
+#ifdef SWM_DEBUG
+char   *get_stack_mode_name(uint8_t);
+#endif
+int32_t         get_swm_iconic(struct ws_win *);
+char   *get_win_name(xcb_window_t);
+void    get_wm_protocols(struct ws_win *);
+int     get_ws_idx(xcb_window_t);
+uint32_t getstate(xcb_window_t);
+void    grabbuttons(struct ws_win *);
+void    grabkeys(void);
+void    grab_windows(void);
+void    iconify(struct swm_region *, union arg *);
+int     isxlfd(char *);
+void    keypress(xcb_key_press_event_t *);
+int     key_cmp(struct key *, struct key *);
+void    key_insert(unsigned int, KeySym, enum keyfuncid, const char *);
 struct key     *key_lookup(unsigned int, KeySym);
+void    key_remove(struct key *);
+void    key_replace(struct key *, unsigned int, KeySym, enum keyfuncid,
+            const char *);
+void    kill_bar_extra_atexit(void);
+void    kill_refs(struct ws_win *);
 #ifdef SWM_DEBUG
-char           *get_atom_name(xcb_atom_t);
-char           *get_notify_detail_label(uint8_t);
-char           *get_notify_mode_label(uint8_t);
-char           *get_stack_mode_name(uint8_t);
+void    leavenotify(xcb_leave_notify_event_t *);
 #endif
-
+void    load_float_geom(struct ws_win *);
+struct ws_win  *manage_window(xcb_window_t, uint16_t);
+void    map_window(struct ws_win *);
+void    mapnotify(xcb_map_notify_event_t *);
+void    mappingnotify(xcb_mapping_notify_event_t *);
+void    maprequest(xcb_map_request_event_t *);
+void    maximize_toggle(struct swm_region *, union arg *);
+void    motionnotify(xcb_motion_notify_event_t *);
+void    move(struct ws_win *, union arg *);
+void    move_step(struct swm_region *, union arg *);
+uint32_t name_to_pixel(int, const char *);
+void    name_workspace(struct swm_region *, union arg *);
+void    new_region(struct swm_screen *, int, int, int, int);
+int     parsekeys(const char *, unsigned int, unsigned int *, KeySym *);
+int     parsequirks(const char *, unsigned long *);
+int     parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *);
+void    pressbutton(struct swm_region *, union arg *);
+void    priorws(struct swm_region *, union arg *);
+#ifdef SWM_DEBUG
+void    print_win_geom(xcb_window_t);
+#endif
+void    propertynotify(xcb_property_notify_event_t *);
+void    quirk_free(struct quirk *);
+void    quirk_insert(const char *, const char *, const char *,unsigned long);
+void    quirk_remove(struct quirk *);
+void    quirk_replace(struct quirk *, const char *, const char *, const char *,
+            unsigned long);
+void    quit(struct swm_region *, union arg *);
+void    raise_toggle(struct swm_region *, union arg *);
+void    region_containment(struct ws_win *, struct swm_region *, int);
 struct swm_region      *region_under(struct swm_screen *, int, int);
+void    regionize(struct ws_win *, int, int);
+void    resize(struct ws_win *, union arg *);
+void    resize_step(struct swm_region *, union arg *);
+void    restart(struct swm_region *, union arg *);
 struct swm_region      *root_to_region(xcb_window_t, int);
+void    screenchange(xcb_randr_screen_change_notify_event_t *);
+void    scan_xrandr(int);
+void    search_do_resp(void);
+void    search_resp_name_workspace(const char *, size_t);
+void    search_resp_search_window(const char *);
+void    search_resp_search_workspace(const char *);
+void    search_resp_uniconify(const char *, size_t);
+void    search_win(struct swm_region *, union arg *);
+void    search_win_cleanup(void);
+void    search_workspace(struct swm_region *, union arg *);
+void    send_to_rg(struct swm_region *, union arg *);
+void    send_to_ws(struct swm_region *, union arg *);
+void    set_region(struct swm_region *);
+int     setautorun(const char *, const char *, int);
+int     setconfbinding(const char *, const char *, int);
+int     setconfcolor(const char *, const char *, int);
+int     setconfmodkey(const char *, const char *, int);
+int     setconfquirk(const char *, const char *, int);
+int     setconfregion(const char *, const char *, int);
+int     setconfspawn(const char *, const char *, int);
+int     setconfvalue(const char *, const char *, int);
+void    setkeybinding(unsigned int, KeySym, enum keyfuncid, const char *);
+int     setkeymapping(const char *, const char *, int);
+int     setlayout(const char *, const char *, int);
+void    setquirk(const char *, const char *, const char *,unsigned long);
+void    setscreencolor(const char *, int, int);
+void    setspawn(const char *, const char *, int);
+void    setup_ewmh(void);
+void    setup_globals(void);
+void    setup_keys(void);
+void    setup_quirks(void);
+void    setup_screens(void);
+void    setup_spawn(void);
+void    set_child_transient(struct ws_win *, xcb_window_t *);
+void    set_swm_iconic(struct ws_win *, int);
+void    set_win_state(struct ws_win *, uint16_t);
+void    shutdown_cleanup(void);
+void    sighdlr(int);
+void    socket_setnonblock(int);
+void    sort_windows(struct ws_win_list *);
+void    spawn(int, union arg *, int);
+void    spawn_custom(struct swm_region *, union arg *, const char *);
+int     spawn_expand(struct swm_region *, union arg *, const char *, char ***);
+void    spawn_insert(const char *, const char *, int);
 struct spawn_prog      *spawn_find(const char *);
+void    spawn_remove(struct spawn_prog *);
+void    spawn_replace(struct spawn_prog *, const char *, const char *, int);
+void    spawn_select(struct swm_region *, union arg *, const char *, int *);
+void    stack_config(struct swm_region *, union arg *);
+void    stack_floater(struct ws_win *);
+void    stack_master(struct workspace *, struct swm_geometry *, int, int);
+void    reorder_window(struct ws_win *, uint32_t);
+void    store_float_geom(struct ws_win *);
+char   *strdupsafe(const char *);
+void    swapwin(struct swm_region *, union arg *);
+void    switchws(struct swm_region *, union arg *);
+void    teardown_ewmh(void);
+void    unescape_selector(char *);
+void    unfocus_win(struct ws_win *);
+void    uniconify(struct swm_region *, union arg *);
+void    unmanage_window(struct ws_win *);
+void    unmapnotify(xcb_unmap_notify_event_t *);
+void    unmap_all(void);
+void    unmap_window(struct ws_win *);
+void    updatenumlockmask(void);
+void    update_modkey(unsigned int);
+void    update_window(struct ws_win *);
+void    validate_spawns(void);
+int     validate_win(struct ws_win *);
+int     validate_ws(struct workspace *);
+void    version(struct swm_region *, union arg *);
+void    win_to_ws(struct ws_win *, int, int);
+pid_t   window_get_pid(xcb_window_t);
+void    wkill(struct swm_region *, union arg *);
+void    workaround(void);
+void    xft_init(struct swm_region *);
+void    _add_startup_exception(const char *, va_list);
+void    add_startup_exception(const char *, ...);
 
 RB_PROTOTYPE(key_tree, key, entry, key_cmp);
 RB_GENERATE(key_tree, key, entry, key_cmp);
-struct key_tree                keys;
+struct key_tree                 keys;
 
 void
 cursors_load(void)
@@ -1134,11 +1152,11 @@ cursors_cleanup(void)
 char *
 expand_tilde(const char *s)
 {
-       struct passwd           *ppwd;
-       int                     i;
+       struct passwd           *ppwd;
+       int                     i;
        long                    max;
-       char                    *user;
-       const char              *sc = s;
+       char                    *user;
+       const char              *sc = s;
        char                    *result;
 
        if (s == NULL)
@@ -1471,9 +1489,9 @@ ewmh_update_actions(struct ws_win *win)
            ewmh[_NET_WM_ALLOWED_ACTIONS].atom, XCB_ATOM_ATOM, 32, 1, actions);
 }
 
-#define _NET_WM_STATE_REMOVE   0       /* remove/unset property */
-#define _NET_WM_STATE_ADD      1       /* add/set property */
-#define _NET_WM_STATE_TOGGLE   2       /* toggle property */
+#define _NET_WM_STATE_REMOVE   0    /* remove/unset property */
+#define _NET_WM_STATE_ADD      1    /* add/set property */
+#define _NET_WM_STATE_TOGGLE   2    /* toggle property */
 
 void
 ewmh_update_win_state(struct ws_win *win, xcb_atom_t state, long action)
@@ -1481,12 +1499,19 @@ ewmh_update_win_state(struct ws_win *win, xcb_atom_t state, long action)
        unsigned int            mask = 0;
        unsigned int            changed = 0;
        unsigned int            orig_flags;
+#ifdef SWM_DEBUG
+       char                    *name;
+#endif
 
        if (win == NULL)
                return;
 
-       DNPRINTF(SWM_D_PROP, "ewmh_update_win_state: window: 0x%x, state: %ld, "
-           "action: %ld\n", win->id, (unsigned long)state, action);
+#ifdef SWM_DEBUG
+       name = get_atom_name(state);
+       DNPRINTF(SWM_D_PROP, "ewmh_update_win_state: window: 0x%x, state: %s, "
+           "action: %ld\n", win->id, name, action);
+       free(name);
+#endif
 
        if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
                mask = EWMH_F_FULLSCREEN;
@@ -1908,12 +1933,12 @@ bar_print_legacy(struct swm_region *r, const char *s)
 void
 bar_print(struct swm_region *r, const char *s)
 {
-       size_t          len;
-       xcb_rectangle_t rect;
-       uint32_t        gcv[1];
-       int32_t         x = 0;
-       XGlyphInfo      info;
-       XftDraw         *draw;
+       size_t                          len;
+       xcb_rectangle_t                 rect;
+       uint32_t                        gcv[1];
+       int32_t                         x = 0;
+       XGlyphInfo                      info;
+       XftDraw                         *draw;
 
        len = strlen(s);
 
@@ -2005,11 +2030,13 @@ bar_window_class_instance(char *s, size_t sz, struct swm_region *r)
 }
 
 void
-bar_window_float(char *s, size_t sz, struct swm_region *r)
+bar_window_state(char *s, size_t sz, struct swm_region *r)
 {
        if (r == NULL || r ->ws == NULL || r->ws->focus == NULL)
                return;
-       if (r->ws->focus->floating)
+       if (r->ws->focus->maximized)
+               strlcat(s, "(m)", sz);
+       else if (r->ws->focus->floating)
                strlcat(s, "(f)", sz);
 }
 
@@ -2126,7 +2153,7 @@ bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
                        strlcat(fmtnew, "+T+4<", sz);
                }
                if (window_name_enabled) {
-                       if (r->ws->focus->floating)
+                       if (r->ws->focus->floating || r->ws->focus->maximized)
                                strlcat(fmtnew, "+F ", sz);
                        strlcat(fmtnew, "+64W ", sz);
                }
@@ -2186,7 +2213,7 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
                bar_workspace_name(tmp, sizeof tmp, r);
                break;
        case 'F':
-               bar_window_float(tmp, sizeof tmp, r);
+               bar_window_state(tmp, sizeof tmp, r);
                break;
        case 'I':
                snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
@@ -2853,27 +2880,109 @@ quit(struct swm_region *r, union arg *args)
 }
 
 void
-map_window(struct ws_win *win, xcb_window_t sibling)
+reorder_window(struct ws_win *win, uint32_t mode)
 {
-       uint16_t                mode = XCB_CONFIG_WINDOW_STACK_MODE;
+       struct ws_win           *w = win;
+       struct swm_region       *r;
+       struct workspace        *ws;
        uint32_t                val[2];
-       int                     i = 0;
 
-       /* If sibling is specified, stack right above it. */
-       if (sibling != XCB_WINDOW_NONE) {
-               mode |= XCB_CONFIG_WINDOW_SIBLING;
-               val[i++] = sibling;
+       if (win == NULL || (r = win->ws->r) == NULL)
+               return;
+       ws = win->ws;
+
+       DNPRINTF(SWM_D_EVENT, "reorder_window: win 0x%x, mode: %d\n",
+           win->id, mode);
+
+       switch (mode) {
+       case SWM_STACK_TOP:
+               /* Position above top-most sibling. */
+               TAILQ_FOREACH_REVERSE(w, &ws->winlist, ws_win_list, entry) {
+                       if (w == win || w->iconic)
+                               continue;
+                       if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+                           (w->ewmh_flags & EWMH_F_FULLSCREEN))
+                               continue;
+                       if (!win->maximized && w->maximized)
+                               continue;
+                       if (win->maximized) {
+                               if (w->floating)
+                                       break;
+                               continue;
+                       }
+                       if (win->floating == w->floating)
+                               break;
+               }
+               break;
+       case SWM_STACK_ABOVE:
+               /* Stack above win directly prior. */
+               while ((w = TAILQ_PREV(w, ws_win_list, entry)) != NULL) {
+                       if (w == win || w->iconic)
+                               continue;
+                       if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+                           (w->ewmh_flags & EWMH_F_FULLSCREEN))
+                               continue;
+                       if (win->maximized != w->maximized)
+                               continue;
+                       if (win->floating == w->floating)
+                               break;
+               }
+               break;
+       case SWM_STACK_BELOW:
+               /* Stack above win directly prior. */
+               while ((w = TAILQ_NEXT(w, entry)) != NULL) {
+                       if (w == win || w->iconic)
+                               continue;
+                       if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+                           (w->ewmh_flags & EWMH_F_FULLSCREEN))
+                               continue;
+                       if (win->maximized != w->maximized)
+                               continue;
+                       if (win->floating == w->floating)
+                               break;
+               }
+               break;
+       case SWM_STACK_BOTTOM:
+               /* Position above top-most sibling. */
+               TAILQ_FOREACH(w, &ws->winlist, entry) {
+                       if (w == win || w->iconic)
+                               continue;
+                       if ((win->ewmh_flags & EWMH_F_FULLSCREEN) !=
+                           (w->ewmh_flags & EWMH_F_FULLSCREEN))
+                               continue;
+                       if (win->maximized != w->maximized)
+                               continue;
+                       if (win->floating == w->floating)
+                               break;
+               }
+               break;
+       }
+
+       if (w == NULL) {
+               if (win->floating || (win->ewmh_flags & EWMH_F_FULLSCREEN)
+                   || win->maximized)
+                       val[0] = r->bar->id;
+               else
+                       val[0] = r->id;
+       } else {
+               val[0] = w->id;
        }
+       DNPRINTF(SWM_D_EVENT, "reorder_window: sibling: 0x%x\n", val[0]);
 
-       val[i] = XCB_STACK_MODE_ABOVE;
+       val[1] = XCB_STACK_MODE_ABOVE;
 
+       xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_STACK_MODE |
+           XCB_CONFIG_WINDOW_SIBLING, val);
+}
+
+void
+map_window(struct ws_win *win)
+{
        if (win == NULL)
                return;
 
-       DNPRINTF(SWM_D_EVENT, "map_window: win 0x%x, mapped: %s, "
-           "sibling: 0x%x\n", win->id, YESNO(win->mapped), sibling);
-
-       xcb_configure_window(conn, win->id, mode, val);
+       DNPRINTF(SWM_D_EVENT, "map_window: win 0x%x, mapped: %s\n",
+           win->id, YESNO(win->mapped));
 
        if (win->mapped)
                return;
@@ -3220,42 +3329,6 @@ validate_ws(struct workspace *testws)
        return (1);
 }
 
-xcb_window_t
-get_sibling(struct ws_win *win, int mode)
-{
-       struct ws_win           *w = win;
-
-       switch (mode) {
-       case SWM_STACK_TOP:
-               TAILQ_FOREACH_REVERSE(w, &w->ws->winlist, ws_win_list, entry)
-                       if (w != win && !w->floating && !w->iconic)
-                               break;
-               break;
-       case SWM_STACK_ABOVE:
-               do {
-                       w = TAILQ_NEXT(w, entry);
-               } while (w != NULL && (w == win || w->floating || w->iconic));
-               break;
-       case SWM_STACK_BELOW:
-               do {
-                       w = TAILQ_PREV(w, ws_win_list, entry);
-               } while (w != NULL && (w == win || w->floating || w->iconic));
-               break;
-       case SWM_STACK_BOTTOM:
-               TAILQ_FOREACH(w, &w->ws->winlist, entry)
-                       if (w != win && !w->floating && !w->iconic)
-                               break;
-               break;
-       default:
-               w = NULL;
-       }
-
-       if (w == NULL)
-               return (win->ws->r->id);
-       else
-               return (w->id);
-}
-
 void
 unfocus_win(struct ws_win *win)
 {
@@ -3287,8 +3360,8 @@ unfocus_win(struct ws_win *win)
        }
 
        if (win->ws->focus == win) {
-               if (tile_gap < 0 && !win->floating)
-                       map_window(win, get_sibling(win, SWM_STACK_BELOW));
+               if (tile_gap < 0 && !win->floating && !win->maximized)
+                       reorder_window(win, SWM_STACK_ABOVE);
                win->ws->focus = NULL;
                win->ws->focus_prev = win;
        }
@@ -3393,30 +3466,35 @@ focus_win(struct ws_win *win)
                    ws->always_raise) {
                        /* If a parent exists, map it first. */
                        if (parent) {
-                               map_window(parent, XCB_WINDOW_NONE);
+                               reorder_window(parent, SWM_STACK_TOP);
+                               map_window(parent);
 
                                /* Map siblings next. */
                                TAILQ_FOREACH(w, &ws->winlist, entry)
                                        if (w != win && !w->iconic &&
-                                           w->transient == parent->id)
-                                               map_window(w, XCB_WINDOW_NONE);
+                                           w->transient == parent->id) {
+                                               reorder_window(w, SWM_STACK_ABOVE);
+                                               map_window(w);
+                                       }
                        }
 
                        /* Map focused window. */
-                       map_window(win, XCB_WINDOW_NONE);
+                       reorder_window(win, SWM_STACK_TOP);
+                       map_window(win);
 
                        /* Finally, map children of focus window. */
                        TAILQ_FOREACH(w, &ws->winlist, entry)
-                               if (w->transient == win->id && !w->iconic)
-                                       map_window(w, XCB_WINDOW_NONE);
+                               if (w->transient == win->id && !w->iconic) {
+                                       reorder_window(w, SWM_STACK_ABOVE);
+                                       map_window(w);
+                               }
                } else if (tile_gap < 0 && !win->floating) {
                        /*
                         * Windows overlap in the layout.
                         * Raise focused win above all tiled wins.
                         */
-                       if (tile_gap < 0 && !win->floating)
-                               map_window(win,
-                                   get_sibling(win, SWM_STACK_TOP));
+                       reorder_window(win, SWM_STACK_TOP);
+                       map_window(win);
                }
 
                set_region(ws->r);
@@ -3594,12 +3672,15 @@ switchws(struct swm_region *r, union arg *args)
                new_ws->focus_prev = NULL;
        }
 
+       new_ws->state = SWM_WS_STATE_MAPPING;
        stack();
 
        /* unmap old windows */
-       if (unmap_old)
+       if (unmap_old) {
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
                        unmap_window(win);
+               old_ws->state = SWM_WS_STATE_HIDDEN;
+       }
 
        /* if workspaces were swapped, then don't wait to set focus */
        if (old_ws->r && focus_mode != SWM_FOCUS_FOLLOW) {
@@ -3617,6 +3698,7 @@ switchws(struct swm_region *r, union arg *args)
        }
 
        focus_flush();
+       new_ws->state = SWM_WS_STATE_MAPPED;
 
        DNPRINTF(SWM_D_WS, "switchws: done.\n");
 }
@@ -3674,7 +3756,7 @@ cyclews(struct swm_region *r, union arg *args)
 void
 priorws(struct swm_region *r, union arg *args)
 {
-       union arg       a;
+       union arg               a;
 
        (void)args;
 
@@ -3782,6 +3864,8 @@ swapwin(struct swm_region *r, union arg *args)
        if (cur_focus == NULL)
                return;
 
+       clear_maximized(r->ws);
+
        source = cur_focus;
        wl = &source->ws->winlist;
 
@@ -3857,14 +3941,14 @@ get_focus_prev(struct ws_win *win)
        wl = &ws->winlist;
        cur_focus = ws->focus;
 
-       DNPRINTF(SWM_D_FOCUS, "get_focus_prev: window: 0x%x, cur_focus: 0x%x\n",
-           WINID(win), WINID(cur_focus));
+       DNPRINTF(SWM_D_FOCUS, "get_focus_prev: window: 0x%x, cur_focus: 0x%x, "
+           "focus_prev: 0x%x\n", WINID(win), WINID(cur_focus),
+           WINID(ws->focus_prev));
 
        /* pickle, just focus on whatever */
        if (cur_focus == NULL) {
                /* use prev_focus if valid */
-               if (ws->focus_prev && ws->focus_prev != cur_focus &&
-                   find_window(WINID(ws->focus_prev)))
+               if (ws->focus_prev && find_window(ws->focus_prev->id))
                        winfocus = ws->focus_prev;
                goto done;
        }
@@ -4084,6 +4168,9 @@ focus(struct swm_region *r, union arg *args)
                goto out;
        }
 
+       if (clear_maximized(ws) > 0)
+               stack();
+
        focus_win(get_focus_magic(winfocus));
        focus_flush();
 
@@ -4105,6 +4192,8 @@ cycle_layout(struct swm_region *r, union arg *args)
        if (ws->cur_layout->l_stack == NULL)
                ws->cur_layout = &layouts[0];
 
+       clear_maximized(ws);
+
        stack();
        bar_draw();
 
@@ -4121,6 +4210,9 @@ stack_config(struct swm_region *r, union arg *args)
        DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n",
            args->id, ws->idx);
 
+       if (clear_maximized(ws) > 0)
+               stack();
+
        if (ws->cur_layout->l_config != NULL)
                ws->cur_layout->l_config(ws, args->id);
 
@@ -4148,9 +4240,6 @@ stack(void) {
                j = 0;
 #endif
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       DNPRINTF(SWM_D_STACK, "stack: workspace: %d "
-                           "(screen: %d, region: %d)\n", r->ws->idx, i, j++);
-
                        /* Adjust stack area for region bar and padding. */
                        g = r->g;
                        g.x += region_padding;
@@ -4162,6 +4251,11 @@ stack(void) {
                                        g.y += bar_height;
                                g.h -= bar_height;
                        }
+
+                       DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: "
+                           "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n",
+                           r->ws->idx, i, j++, g.x, g.y, g.w, g.h);
+
                        r->ws->cur_layout->l_stack(r->ws, &g);
                        r->ws->cur_layout->l_string(r->ws);
                        /* save r so we can track region changes */
@@ -4211,68 +4305,80 @@ load_float_geom(struct ws_win *win)
 }
 
 void
-stack_floater(struct ws_win *win, struct swm_region *r)
+stack_floater(struct ws_win *win)
 {
+       struct workspace        *ws;
+       struct swm_region       *r;
+
        if (win == NULL)
                return;
 
-       DNPRINTF(SWM_D_MISC, "stack_floater: window: 0x%x\n", win->id);
+       ws = win->ws;
 
-       /*
-        * to allow windows to change their size (e.g. mplayer fs) only retrieve
-        * geom on ws switches or return from max mode
-        */
-       if (win->floatmaxed || (r != r->ws->old_r &&
-           !(win->ewmh_flags & EWMH_F_FULLSCREEN))) {
-               /* update geometry for the new region */
-               load_float_geom(win);
-       }
+       if ((r = ws->r) == NULL)
+               return;
 
-       win->floatmaxed = 0;
+       DNPRINTF(SWM_D_MISC, "stack_floater: window: 0x%x\n", win->id);
 
-       /*
-        * if set to fullscreen mode, configure window to maximum size.
-        */
        if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
+               /* _NET_WM_FULLSCREEN: fullscreen without border. */
                if (!win->g_floatvalid)
                        store_float_geom(win);
 
                win->g = r->g;
-       }
+               win->bordered = 0;
+       } else if (win->maximized) {
+               /* Maximize: like a single stacked window. */
+               if (!win->g_floatvalid)
+                       store_float_geom(win);
 
-       /*
-        * remove border on fullscreen floater when in fullscreen mode or when
-        * the quirk is present.
-        */
-       if ((win->ewmh_flags & EWMH_F_FULLSCREEN) ||
-           ((win->quirks & SWM_Q_FULLSCREEN) &&
-            (WIDTH(win) >= WIDTH(r)) && (HEIGHT(win) >= HEIGHT(r)))) {
-               if (win->bordered) {
+               win->g = r->g;
+
+               if (bar_enabled && ws->bar_enabled) {
+                       win->bordered = 1;
+                       if (!bar_at_bottom)
+                               Y(win) += bar_height;
+                       HEIGHT(win) -= bar_height;
+               } else if (disable_border) {
                        win->bordered = 0;
-                       X(win) += border_width;
-                       Y(win) += border_width;
+               } else {
+                       win->bordered = 1;
                }
-       } else if (!win->bordered) {
-               win->bordered = 1;
-               X(win) -= border_width;
-               Y(win) -= border_width;
-       }
-
-       if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
-               WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
-               HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
-       }
 
-       if (!win->manual && !(win->ewmh_flags & EWMH_F_FULLSCREEN) &&
-           !(win->quirks & SWM_Q_ANYWHERE)) {
-               /*
-                * floaters and transients are auto-centred unless moved,
-                * resized or ANYWHERE quirk is set.
-                */
-               X(win) = X(r) + (WIDTH(r) - WIDTH(win)) /  2 - BORDER(win);
-               Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - BORDER(win);
+               if (win->bordered) {
+                       HEIGHT(win) -= 2 * border_width;
+                       WIDTH(win) -= 2 * border_width;
+               }
+       } else {
+               /* Normal floating window. */
+               /* Update geometry if new region. */
+               if (r != ws->old_r)
+                       load_float_geom(win);
+
+               if ((win->quirks & SWM_Q_FULLSCREEN) &&
+                   WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) {
+                       /* Remove border for FULLSCREEN quirk. */
+                       win->bordered = 0;
+               } else if (!win->manual) {
+                       if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
+                               /* Adjust size on TRANSSZ quirk. */
+                               WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
+                               HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
+                       }
 
-               store_float_geom(win);
+                       if (!(win->quirks & SWM_Q_ANYWHERE)) {
+                               /*
+                                * Floaters and transients are auto-centred
+                                * unless manually moved, resized or ANYWHERE
+                                * quirk is set.
+                                */
+                               X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 -
+                                   BORDER(win);
+                               Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 -
+                                   BORDER(win);
+                               store_float_geom(win);
+                       }
+               }
        }
 
        /* Ensure at least 1 pixel of the window is in the region. */
@@ -4319,7 +4425,7 @@ void
 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
 {
        struct swm_geometry     win_g, r_g = *g;
-       struct ws_win           *win, *fs_win = NULL;
+       struct ws_win           *win, *fs_win = NULL, *max_win = NULL;
        int                     i, j, s, stacks;
        int                     w_inc = 1, h_inc, w_base = 1, h_base;
        int                     hrh, extra = 0, h_slice, last_h = 0;
@@ -4334,6 +4440,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        if (winno == 0 && count_win(ws, 1) == 0)
                return;
 
+       /* Find first tiled window. */
        TAILQ_FOREACH(win, &ws->winlist, entry)
                if (!win->transient && !win->floating && !win->iconic)
                        break;
@@ -4400,6 +4507,11 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        continue;
                }
 
+               if (win->maximized) {
+                       max_win = win;
+                       continue;
+               }
+
                if (split && i == split) {
                        colno = (winno - mwin) / stacks;
                        if (s <= (winno - mwin) % stacks)
@@ -4496,7 +4608,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        update_window(win);
                }
 
-               map_window(win, get_sibling(win, SWM_STACK_BELOW));
+               reorder_window(win, SWM_STACK_ABOVE);
+               map_window(win);
 
                last_h = win_g.h;
                i++;
@@ -4504,30 +4617,45 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        }
 
        /* Map/raise focused tiled window to top if windows could overlap. */
-       if (tile_gap < 0 && ws->focus != NULL && !ws->focus->floating)
-               map_window(ws->focus, get_sibling(ws->focus, SWM_STACK_TOP));
+       if (tile_gap < 0 && ws->focus != NULL && !ws->focus->floating &&
+           !ws->focus->maximized) {
+               reorder_window(ws->focus, SWM_STACK_TOP);
+               map_window(ws->focus);
+       }
 
 notiles:
        /* now, stack all the floaters and transients */
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (!win->transient && !win->floating)
-                       continue;
-               if (win->iconic)
+               if ((!win->transient && !win->floating) || win->iconic)
                        continue;
+
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
                        fs_win = win;
                        continue;
                }
 
-               stack_floater(win, ws->r);
-               map_window(win, XCB_WINDOW_NONE);
+               if (win->maximized) {
+                       max_win = win;
+                       continue;
+               }
+
+               stack_floater(win);
+               reorder_window(win, SWM_STACK_ABOVE);
+               map_window(win);
        }
 
-       /* Make sure fs_win is stacked last so it's on top. */
+       /* Make sure fs_win is stacked on top. */
        if (fs_win) {
-               stack_floater(fs_win, ws->r);
-               map_window(fs_win, XCB_WINDOW_NONE);
+               stack_floater(fs_win);
+               reorder_window(fs_win, SWM_STACK_TOP);
+               map_window(fs_win);
+       } else if (max_win) {
+               stack_floater(max_win);
+               reorder_window(max_win, SWM_STACK_TOP);
+               map_window(max_win);
        }
+
+       DNPRINTF(SWM_D_STACK, "stack_master: max_win: 0x%x\n", WINID(max_win));
 }
 
 void
@@ -4668,16 +4796,18 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                if (w->transient || w->iconic)
                        continue;
 
-               if (!w->mapped && w != win)
-                       map_window(w, XCB_WINDOW_NONE);
+               if (!w->mapped && w != win) {
+                       reorder_window(w, SWM_STACK_TOP);
+                       map_window(w);
+               }
 
-               if (w->floating && !w->floatmaxed) {
+               if (w->floating && !w->maximized) {
                        /*
                         * retain geometry for retrieval on exit from
                         * max_stack mode
                         */
                        store_float_geom(w);
-                       w->floatmaxed = 1;
+                       w->maximized = 1;
                }
 
                /* only reconfigure if necessary */
@@ -4698,25 +4828,29 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 
        /* If a parent exists, map/raise it first. */
        if (parent) {
-               map_window(parent, XCB_WINDOW_NONE);
+               reorder_window(parent, SWM_STACK_TOP);
+               map_window(parent);
 
                /* Map siblings next. */
                TAILQ_FOREACH(w, &ws->winlist, entry)
                        if (w != win && !w->iconic &&
                            w->transient == parent->id) {
-                               stack_floater(w, ws->r);
-                               map_window(w, XCB_WINDOW_NONE);
+                               stack_floater(w);
+                               reorder_window(w, SWM_STACK_ABOVE);
+                               map_window(w);
                        }
        }
 
        /* Map/raise focused window. */
-       map_window(win, XCB_WINDOW_NONE);
+       reorder_window(win, SWM_STACK_TOP);
+       map_window(win);
 
        /* Finally, map/raise children of focus window. */
        TAILQ_FOREACH(w, &ws->winlist, entry)
                if (w->transient == win->id && !w->iconic) {
-                       stack_floater(w, ws->r);
-                       map_window(w, XCB_WINDOW_NONE);
+                       stack_floater(w);
+                       reorder_window(w, SWM_STACK_ABOVE);
+                       map_window(w);
                }
 }
 
@@ -4776,8 +4910,16 @@ send_to_ws(struct swm_region *r, union arg *args)
 
        DNPRINTF(SWM_D_MOVE, "send_to_ws: win 0x%x, ws %d\n", win->id, wsid);
 
+       if (wsid >= workspace_limit)
+               return;
+
+       if (win->ws->idx == wsid)
+               return;
+
        win_to_ws(win, wsid, 1);
 
+       win->maximized = 0;
+
        /* Restack and set new focus. */
        stack();
 
@@ -4902,6 +5044,9 @@ raise_toggle(struct swm_region *r, union arg *args)
        if (r == NULL || r->ws == NULL)
                return;
 
+       if (r->ws->focus && r->ws->focus->maximized)
+               return;
+
        r->ws->always_raise = !r->ws->always_raise;
 
        /* bring floaters back to top */
@@ -5373,29 +5518,83 @@ wkill(struct swm_region *r, union arg *args)
 }
 
 int
-floating_toggle_win(struct ws_win *win)
+clear_maximized(struct workspace *ws)
+{
+       struct ws_win           *w;
+       int                     count = 0;
+
+       /* Clear any maximized win(s) on ws. */
+       TAILQ_FOREACH(w, &ws->winlist, entry)
+               if (w->maximized) {
+                       w->maximized = 0;
+                       if (w->floating)
+                               load_float_geom(w);
+                       ++count;
+               }
+
+       return count;
+}
+
+void
+maximize_toggle(struct swm_region *r, union arg *args)
 {
+       struct ws_win           *win = r->ws->focus;
+
+       /* suppress unused warning since var is needed */
+       (void)args;
+
        if (win == NULL)
-               return (0);
+               return;
 
-       if (win->ws->r == NULL)
+       DNPRINTF(SWM_D_MISC, "maximize_toggle: win %#x\n", win->id);
+
+       if (win->ewmh_flags & EWMH_F_FULLSCREEN)
+               return;
+
+       if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
+               return;
+
+       if (win->floating) {
+               if (win->maximized)
+                       load_float_geom(win);
+               else
+                       store_float_geom(win);
+       }
+
+       win->maximized = !win->maximized;
+
+       stack();
+
+       if (win == win->ws->focus)
+               focus_win(win);
+
+       focus_flush();
+       DNPRINTF(SWM_D_MISC, "maximize_toggle: done\n");
+}
+
+int
+floating_toggle_win(struct ws_win *win)
+{
+       if (win == NULL)
                return (0);
 
        /* reject floating toggles in max stack mode */
        if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
                return (0);
 
+       if (win->ws->r == NULL)
+               return (0);
+
        if (win->floating) {
-               if (!win->floatmaxed) {
-                       /* retain position for refloat */
+               if (!win->maximized)
                        store_float_geom(win);
-               }
-               win->floating = 0;
        } else {
                load_float_geom(win);
-               win->floating = 1;
        }
 
+       win->floating = !win->floating;
+       win->maximized = 0;
+
        ewmh_update_actions(win);
 
        return (1);
@@ -5565,13 +5764,17 @@ resize(struct ws_win *win, union arg *args)
        if (!win->transient && !win->floating)
                return;
 
-       /* reject resizes in max mode for floaters (transient ok) */
-       if (win->floatmaxed)
+       /* In max_stack mode, should only resize transients. */
+       if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
                return;
 
-       win->manual = 1;
-       ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
-           _NET_WM_STATE_ADD);
+       win->maximized = 0;
+
+       if (!win->manual) {
+               win->manual = 1;
+               ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
+                   _NET_WM_STATE_ADD);
+       }
 
        stack();
 
@@ -5787,6 +5990,8 @@ move(struct ws_win *win, union arg *args)
        if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
                return;
 
+       win->maximized = 0;
+
        if (!win->manual) {
                win->manual = 1;
                ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
@@ -5937,6 +6142,7 @@ struct keyfunc {
        { "focus_next",         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
        { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
        { "focus_urgent",       focus,          {.id = SWM_ARG_ID_FOCUSURGENT} },
+       { "maximize_toggle",    maximize_toggle,{0} },
        { "height_grow",        resize_step,    {.id = SWM_ARG_ID_HEIGHTGROW} },
        { "height_shrink",      resize_step,    {.id = SWM_ARG_ID_HEIGHTSHRINK} },
        { "iconify",            iconify,        {0} },
@@ -6698,6 +6904,7 @@ setup_keys(void)
        setkeybinding(MODKEY,           XK_k,           KF_FOCUS_PREV,  NULL);
        setkeybinding(MODKEY_SHIFT,     XK_Tab,         KF_FOCUS_PREV,  NULL);
        setkeybinding(MODKEY,           XK_u,           KF_FOCUS_URGENT,NULL);
+       setkeybinding(MODKEY,           XK_e,           KF_MAXIMIZE_TOGGLE,NULL);
        setkeybinding(MODKEY_SHIFT,     XK_equal,       KF_HEIGHT_GROW,NULL);
        setkeybinding(MODKEY_SHIFT,     XK_minus,       KF_HEIGHT_SHRINK,NULL);
        setkeybinding(MODKEY,           XK_w,           KF_ICONIFY,     NULL);
@@ -8080,7 +8287,7 @@ manage_window(xcb_window_t id, uint16_t mapped)
        Y(win) = gr->y + gr->border_width - border_width;
        win->bordered = 1;
        win->mapped = mapped;
-       win->floatmaxed = 0;
+       win->maximized = 0;
        win->ewmh_flags = 0;
        win->s = r->s;  /* this never changes */
 
@@ -8555,7 +8762,7 @@ configurerequest(xcb_configure_request_event_t *e)
                        xcb_flush(conn);
                }
        } else if ((!win->manual || win->quirks & SWM_Q_ANYWHERE) &&
-           !(win->ewmh_flags & EWMH_F_FULLSCREEN)) {
+           !(win->ewmh_flags & EWMH_F_FULLSCREEN) && !win->maximized) {
                if (win->ws->r)
                        r = win->ws->r;
                else if (win->ws->old_r)
@@ -8585,12 +8792,13 @@ configurerequest(xcb_configure_request_event_t *e)
                win->g_floatvalid = 1;
 
                if (win->floating && r && (win->transient ||
-                   win->ws->cur_layout != &layouts[SWM_MAX_STACK])) {
+                   win->ws->cur_layout != &layouts[SWM_MAX_STACK])
+                   && !win->maximized) {
                        WIDTH(win) = win->g_float.w;
                        HEIGHT(win) = win->g_float.h;
 
                        if (r) {
-                               stack_floater(win, r);
+                               stack_floater(win);
                                focus_flush();
                        }
                } else {
@@ -8795,20 +9003,33 @@ leavenotify(xcb_leave_notify_event_t *e)
 void
 mapnotify(xcb_map_notify_event_t *e)
 {
-       struct ws_win           *win;
+       struct ws_win           *win, *parent = NULL;
+       struct workspace        *ws;
 
        DNPRINTF(SWM_D_EVENT, "mapnotify: window: 0x%x\n", e->window);
 
        if ((win = manage_window(e->window, 1)) == NULL)
                return;
+       ws = win->ws;
+
+       /* Need to know if win was mapped due to ws switch. */
+       if (ws->state == SWM_WS_STATE_MAPPED) {
+               if (ws->focus_pending && ws->focus_pending->transient)
+                       parent = find_window(win->transient);
+
+               /* If window's parent is maximized, don't clear it. */
+               if ((parent == NULL) || !parent->maximized)
+                       if (clear_maximized(ws) > 0)
+                               stack();
+       }
 
        win->mapped = 1;
        set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
 
        if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) {
-               if (win->ws->focus_pending == win) {
+               if (ws->focus_pending == win) {
                        focus_win(win);
-                       win->ws->focus_pending = NULL;
+                       ws->focus_pending = NULL;
                        focus_flush();
                }
        }
@@ -9116,7 +9337,7 @@ unmapnotify(xcb_unmap_notify_event_t *e)
 void
 clientmessage(xcb_client_message_event_t *e)
 {
-       struct ws_win           *win;
+       struct ws_win *win;
        xcb_map_request_event_t mre;
 #ifdef SWM_DEBUG
        char                    *name;
@@ -9318,6 +9539,9 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
        if (ws == NULL)
                errx(1, "new_region: no free workspaces");
 
+       if (ws->state == SWM_WS_STATE_HIDDEN)
+               ws->state = SWM_WS_STATE_MAPPING;
+
        X(r) = x;
        Y(r) = y;
        WIDTH(r) = w;
@@ -9427,6 +9651,7 @@ out:
        TAILQ_FOREACH(r, &screens[i].orl, entry) {
                TAILQ_FOREACH(win, &r->ws->winlist, entry)
                        unmap_window(win);
+               r->ws->state = SWM_WS_STATE_HIDDEN;
 
                /* The screen shouldn't focus on an unused region. */
                if (screens[i].r_focus == r)
@@ -9477,6 +9702,11 @@ screenchange(xcb_randr_screen_change_notify_event_t *e)
 
        bar_draw();
        focus_flush();
+
+       /* Update workspace state on all regions. */
+       for (i = 0; i < num_screens; i++)
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
+                       r->ws->state = SWM_WS_STATE_MAPPED;
 }
 
 void
@@ -9585,10 +9815,10 @@ grab_windows(void)
 void
 setup_screens(void)
 {
-       int                                     i, j, k, num_screens;
-       struct workspace                        *ws;
-       uint32_t                                gcv[1], wa[1];
-       const xcb_query_extension_reply_t       *qep;
+       int                     i, j, k, num_screens;
+       struct workspace        *ws;
+       uint32_t                gcv[1], wa[1];
+       const xcb_query_extension_reply_t *qep;
        xcb_screen_t                            *screen;
        xcb_randr_query_version_cookie_t        c;
        xcb_randr_query_version_reply_t         *r;
@@ -9659,6 +9889,7 @@ setup_screens(void)
                        ws->focus_pending = NULL;
                        ws->r = NULL;
                        ws->old_r = NULL;
+                       ws->state = SWM_WS_STATE_HIDDEN;
                        TAILQ_INIT(&ws->winlist);
                        TAILQ_INIT(&ws->unmanagedlist);
 
@@ -9841,7 +10072,7 @@ main(int argc, char *argv[])
        int                     xfd, i, num_screens, startup = 1;
        struct sigaction        sact;
        xcb_generic_event_t     *evt;
-       struct timeval          tv;
+       struct timeval          tv;
        fd_set                  rd;
        int                     rd_max;
        int                     stdin_ready = 0;