[
  {
    "path": ".github/CONTRIBUTING",
    "content": "CONTRIBUTING\n============\n\nWant to contribute? Great! First, read this page.\n\nCode reviews:\n*   All submissions, including submissions by project members, require\n    review. We use Github pull requests for this purpose.\n\nSome tips for good pull requests:\n*   Use our code. When in doubt, try to stay true to the existing code\n    of the project.\n*   Write a descriptive commit message. What problem are you solving\n    and what are the consequences? Where and what did you test? Some\n    good tips:\n    http://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message\n    https://www.kernel.org/doc/Documentation/SubmittingPatches\n*   If your PR consists of multiple commits which are successive\n    improvements / fixes to your first commit, consider squashing them\n    into a single commit (`git rebase -i`) such that your PR is a\n    single commit on top of the current HEAD. This make reviewing the\n    code so much easier, and our history more readable.\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: mcpcpc\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: https://www.paypal.me/mcpcpc/usd5\n"
  },
  {
    "path": ".github/workflows/codacy-analysis.yml",
    "content": "# This workflow checks out code, performs a Codacy security scan\n# and integrates the results with the\n# GitHub Advanced Security code scanning feature.  For more information on\n# the Codacy security scan action usage and parameters, see\n# https://github.com/codacy/codacy-analysis-cli-action.\n# For more information on Codacy Analysis CLI in general, see\n# https://github.com/codacy/codacy-analysis-cli.\n\nname: Codacy Security Scan\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  codacy-security-scan:\n    name: Codacy Security Scan\n    runs-on: ubuntu-latest\n    steps:\n      # Checkout the repository to the GitHub Actions runner\n      - name: Checkout code\n        uses: actions/checkout@v2\n\n      # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis\n      - name: Run Codacy Analysis CLI\n        uses: codacy/codacy-analysis-cli-action@1.1.0\n        with:\n          # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository\n          # You can also omit the token and run the tools that support default configurations\n          project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}\n          verbose: true\n          output: results.sarif\n          format: sarif\n          # Adjust severity of non-security issues\n          gh-code-scanning-compat: true\n          # Force 0 exit code to allow SARIF file generation\n          # This will handover control about PR rejection to the GitHub side\n          max-allowed-issues: 2147483647\n\n      # Upload the SARIF file generated in the previous step\n      - name: Upload SARIF results file\n        uses: github/codeql-action/upload-sarif@v1\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".gitignore",
    "content": "xwm\n*.a\n*.o\n"
  },
  {
    "path": "CHANGELOG",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [0.1.9] - 2022-01-25\n### Added\n- New year to LICENSE file.\n\n### Removed\n- \"PATCHES\" section from the README.\n\n###Fixed\n- Typographic errors in the CHANGELOG.\n\n## [0.1.8] - 2021-03-14\n### Fixed\n- free(ev), which is called erroneously when ev might equal NULL.\n\n### Removed\n- xcb_destroy_window call in the spawn(), which is unnecessary and, furthermore,\n  not recommended per the developer documentation.\n\n## [0.1.7] - 2021-03-03\n###  Fixed\n- Memory leak due to xcb_wait_for_event() not being freed.\n- Order of the CHANGELOG (i.e. latest on top).\n\n## [0.1.6] - 2020-12-28\n### Added\n- Window fullscreen command alias (default: Win+f).\n\n## [0.1.5] - 2020-12-23\n### Removed\n- Unused root padding definitions.\n- xcb_wait_for_event() not being freed.\n\n## [0.1.4] - 2020-12-22\n### Added\n- Raise focus command keys to xwm.1 and README.\n\n### Changed\n- The \"Features\" section of the README to indicate audience.\n\n### Removed\n- Pointer warp on window resize/move as this appears to be default.\n\n### Fixed\n- Spelling and grammar in man page.\n\n## [0.1.3] - 2020-11-30\n### Added\n- Instructions in README for Arch Linux AUR installation.\n\n### Changed\n- Default new window location from top-left to center.\n\n### Removed\n- Global root variable.\n- Removed leave notify event.\n\n## [0.1.2] - 2020-11-23\n### Changed\n- Default surf start page to startpage.com.\n\n### Fixed\n- Zombie reaping.\n\n## [0.1.1] - 2020-11-21\n### Added\n- Makefile warnings (e.g. -Wall, Wextra, etc.).\n\n## [0.1.0] - 2020-11-20\n### Fixed\n- MISRA 10.4 non-compliance.\n- Fix operator dependence.\n\n## [0.0.9] - 2020-11-17\n### Fixed\n- Window resize lag.\n- Bug where window border moves outside of root window.\n\n## [0.0.8] - 2020-11-16\n### Fixed\n- Window focus/unfocus on new window creation.\n\n## [0.0.7] - 2020-11-13\n### Fixed\n- Border focus/unfocus when leaving a window.\n\n## [0.0.6] - 2020-11-12\n### Added\n- Window borders and color definitions.\n\n## [0.0.5] - 2020-11-11\n### Added\n- Default key bindings in man page.\n- Disclaimer section in README.\n\n## [0.0.4] - 2020-11-10\n### Added\n- Instructions for patches and the patching process.\n- Minimum window height and width parameters.\n\n### Removed\n- Unused stdarg.h library dependency.\n\n### Fixed\n- Crash when pointer x/y cords less than window origin.\n\n## [0.0.3] - 2020-11-08\n### Added\n- Examples in the config.h file.\n- Window height and width parameters.\n\n### Removed\n- string.h library dependency.\n\n## [0.0.2] - 2020-11-07\n### Added\n- Close window command (default: Win+q).\n\n### Fixed\n- Resize crash in root window.\n\n## [0.0.1] - 2020-11-06\n### Added\n- Initial release.\n\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-2022 Michael Czigler\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".POSIX:\nALL_WARNING = -Wall -Wextra -pedantic\nALL_LDFLAGS = -lxcb -lxcb-keysyms $(LDFLAGS)\nALL_CFLAGS = $(CPPFLAGS) $(CFLAGS) -std=c99 $(ALL_WARNING)\nPREFIX = /usr/local\nLDLIBS = -lm\nBINDIR = $(PREFIX)/bin\nMANDIR = $(PREFIX)/share/man\n\nall: xwm\ninstall: all\n\tmkdir -p $(DESTDIR)$(BINDIR)\n\tmkdir -p $(DESTDIR)$(MANDIR)/man1\n\tcp -f xwm $(DESTDIR)$(BINDIR)\n\tcp -f xwm.1 $(DESTDIR)$(MANDIR)/man1\n\tchmod 755 $(DESTDIR)$(BINDIR)/xwm\n\tchmod 644 $(DESTDIR)$(MANDIR)/man1/xwm.1\nxwm: xwm.o\n\t$(CC)  -o xwm xwm.o $(ALL_LDFLAGS) $(LDLIBS)\nxwm.o: xwm.c xwm.h config.h\nclean:\n\trm -f xwm *.o\nuninstall:\n\trm -f $(DESTDIR)$(BINDIR)/xwm\n\trm -f $(DESTDIR)$(MANDIR)/man1/xwm.1\n.PHONY: all install uninstall clean\n"
  },
  {
    "path": "README",
    "content": "xwm - a tiny XCB floating window manager\n========================================\n\nA minimal viable solution that was developed with single-monitor\nworkflows in mind. Despite the small footprint, xwm maintains\nextensibility and can be customized to enhance the user experience.\n\nREQUIREMENTS\n------------\n\nxcb-util-keysyms, along with any default or user defined utilities:\n    application launcher   https://git.suckless.org/dmenu\n    terminal emulator      https://git.suckless.org/st\n    internet browser       https://git.suckless.org/surf\n\nCOMMANDS\n--------\n\nDefault keys and behavior can be customized via the config.h file:\n    Win+Button1            raise focused window to top of stack\n    Win+Button3            raise focused window to top of stack\n    Win+Button1+[drag]     interactive window move\n    Win+Button3+[drag]     interactive window resize\n    Win+Space              run launcher menu (default: dmenu_run)\n    Win+Enter              create new terminal window (default: st)\n    Win+b                  create new browser window (default: surf)\n    Win+q                  kill focused window\n    Win+f                  full-screen focused window\n    Win+Shift+q            quit window manager\n\nINSTALL\n-------\n\nBuilding and installing from source:\n    $ git clone https://github.com/mcpcpc/xwm\n    $ cd xwm\n    $ make\n    $ sudo make install\n\nEXAMPLES\n--------\n\nChecking the installed version of xwm:\n    $ xwm -v\n\nUsing startx to run xwm:\n    $ echo \"exec xwm\" > ~/.xinitrc\n    $ startx\n\nUsing imagemagick to set a wallpaper:\n    $ display -window root background.png\n\nPatching xwm:\n    $ patch -p0 < mypatch.patch\n\nDISCLAIMER\n----------\n\nxwm (\"XCB Window Manager\") is an homage to the original and \nnow-defunct \"X Window Manager\". Both projects are unaffiliated \nand do not share any common source code.\n\nCONTACT\n-------\n\nFor questions or issues, please contact michaelczigler[at]mcpcpc[dot]com.\n"
  },
  {
    "path": "config.h",
    "content": "/* DEFAULT KEYS\n * The following are the possible mask definitions.  Note\n * that these definitions may vary between X implementations\n * and keyboard models.\n *     XCB_MOD_MASK_1       -> Alt_L Alt_R Meta_L\n *     XCB_MOD_MASK_2       -> Num_Lock\n *     XCB_MOD_MASK_3       -> ISO_Level3_Shift\n *     XCB_MOD_MASK_4       -> Super_L Super_R SuperL Hyper_L\n *     XCB_MOD_MASK_5       -> ISO_Level5_Shifta\n *     XCB_MOD_MASK_SHIFT\n *     XCB_MOD_MASK_CONTROL\n *     XCB_MOD_MASK_LOCK\n *     XCB_MOD_MASK_ANY\n */\n\n#define MOD1                   XCB_MOD_MASK_4\n#define MOD2                   XCB_MOD_MASK_SHIFT\n\n/* DEFAULT WINDOW PROPERTIES\n * The following parameters can be used to change existing and new\n * window behavior.\n */\n\n#define WINDOW_WIDTH           600      /* pixels */\n#define WINDOW_HEIGHT          400      /* pixels */\n#define WINDOW_MIN_WIDTH       60       /* pixels */\n#define WINDOW_MIN_HEIGHT      40       /* pixels */\n#define BORDER_WIDTH           1        /* 0 = no border effect */\n#define BORDER_COLOR_UNFOCUSED 0x696969 /* 0xRRGGBB */\n#define BORDER_COLOR_FOCUSED   0xFFFFFF /* 0xRRGGBB */\n\n/* ALIASED COMMANDS\n * Each space delimited argument should be passed as an additional\n * value to the character pointer array. For example, to run\n * \"foo -a bar\", the array would be \"{ \"foo\", \"-a\", \"bar\", NULL }\".\n * Since execvp() is a variadic functions, each argument pointer must\n * be terminated by a NULL pointer.\n */\n\nstatic char *termcmd[] = { \"st\", NULL };\nstatic char *menucmd[] = { \"dmenu_run\", NULL };\nstatic char *browcmd[] = { \"surf\", \"https://startpage.com\", NULL };\n\n/* KEY ALIASES\n * In general, one shortcut key should exist per alias. For more key\n * definitions, refer to the keysymdef.h and XF86keysym.h headers.\n */\n\nstatic Key keys[] = {\n\t{ MOD1,      0x0062, spawn,      browcmd }, /* 0x0062 = XK_b */\n\t{ MOD1,      0xff0d, spawn,      termcmd }, /* 0xff0d = XK_Enter */\n\t{ MOD1,      0x0020, spawn,      menucmd }, /* 0x0020 = XK_space */\n\t{ MOD1,      0x0066, fullclient, NULL },    /* 0x0066 = XK_f */\n\t{ MOD1,      0x0071, killclient, NULL },    /* 0x0071 = XK_q */\n\t{ MOD1|MOD2, 0x0071, closewm,    NULL }     /* 0x0071 = XK_q */\n};\n"
  },
  {
    "path": "xwm.1",
    "content": ".\\\"Manpage for xwm.\n.TH XWM 1 \"NOVEMBER 2020\" Linux \"User Manuals\"\n.SH NAME\nxwm \\- a tiny XCB floating window manager\n.SH SYNOPSIS\n.B xwm [-v]\n.SH DESCRIPTION\n.B xwm\nis a tiny floating window manager implemented using\nthe XCB protocol. Other than\n.BR xcb-util-keysyms (1),\nxwm relies on third-party tools, which can be\nconfigured or expanded to meet the user's preference.\nThe default configuration provides support and\ncommand aliases for the\n.BR surf (1)\nweb browser,\n.BR st (1)\nterminal emulator and\n.BR dmenu (1)\napplication launcher.\n.SH OPTIONS\n.IP -v\nPrints the current xwm version.\n.SH DEFAULT KEY BINDINGS\n.B xwm\nmay be controlled from an attached client using a key\ncombination of a prefix key, the 'Super' (Win) key by\ndefault, followed by a command key.\n.PP\nThe default command key bindings are:\n.TS\ntab(;);\nlb l l.\n_\nSuper;MouseButton1;T{\nRaise focused window to top of stack.\nT}\n_\nSuper;MouseButton3;T{\nRaise focused window to top of stack.\nT}\n_\nSuper-MouseButton1;[drag];T{\nInteractive window move.\nT}\n_\nSuper-MouseButton3;[drag];T{\nInteractive window resize.\nT}\n_\nSuper;Space;T{\nRun launcher menu (default: dmenu_run).\nT}\n_\nSuper;Enter;T{\nCreate new terminal window (default: st).\nT}\n_\nSuper;b;T{\nCreate new browser window (default: surf).\nT}\n_\nSuper;f;T{\nFull-screen focused window.\nT}\n_\nSuper;q;T{\nKill focused window.\nT}\n_\nSuper-Shift;q;T{\nQuite window manager.\nT}\n_\n.TE\n.SH BUGS\nNo known bugs.\n.SH AUTHOR\nMichael Czigler <michaelczigler[at]mcpcpc[dot]com>\n.SH \"SEE ALSO\"\n.BR libxcb (1),\n.BR st (1),\n.BR surf (1),\n.BR dmenu (1)\n"
  },
  {
    "path": "xwm.c",
    "content": "/* See LICENSE file for license details. */\n#include <stdlib.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <xcb/xcb.h>\n#include <xcb/xcb_keysyms.h>\n#include \"xwm.h\"\n#include \"config.h\"\n\n#define UNUSED(x) (void)(x)\n\nstatic xcb_connection_t * dpy;\nstatic xcb_screen_t * scre;\nstatic xcb_drawable_t win;\nstatic uint32_t values[3];\n\nstatic void killclient(char **com) {\n\tUNUSED(com);\n\txcb_kill_client(dpy, win);\n}\n\nstatic void closewm(char **com) {\n\tUNUSED(com);\n\tif (dpy != NULL) {\n\t\txcb_disconnect(dpy);\n\t}\n}\n\nstatic void spawn(char **com) {\n\tif (fork() == 0) {\n\t\tsetsid();\n\t\tif (fork() != 0) {\n\t\t\t_exit(0);\n\t\t}\n\t\texecvp((char*)com[0], (char**)com);\n\t\t_exit(0);\n\t}\n\twait(NULL);\n}\n\nstatic void fullclient(char **com) {\n\tUNUSED(com);\n\tuint32_t vals[4];\n\tvals[0] = 0 - BORDER_WIDTH;\n\tvals[1] = 0 - BORDER_WIDTH;\n\tvals[2] = scre->width_in_pixels;\n\tvals[3] = scre->height_in_pixels;\n\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X |\n\t\tXCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |\n\t\tXCB_CONFIG_WINDOW_HEIGHT, vals);\n\txcb_flush(dpy);\n}\n\nstatic void handleButtonPress(xcb_generic_event_t *ev) {\n\txcb_button_press_event_t  *e = (xcb_button_press_event_t *) ev;\n\twin = e->child;\n\tvalues[0] = XCB_STACK_MODE_ABOVE;\n\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);\n\tvalues[2] = ((1 == e->detail) ? 1 : ((win != 0) ? 3 : 0 ));\n\txcb_grab_pointer(dpy, 0, scre->root, XCB_EVENT_MASK_BUTTON_RELEASE\n\t\t| XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT,\n\t\tXCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,\n\t\tscre->root, XCB_NONE, XCB_CURRENT_TIME);\n}\n\nstatic void handleMotionNotify(xcb_generic_event_t *ev) {\n\tUNUSED(ev);\n\txcb_query_pointer_cookie_t coord = xcb_query_pointer(dpy, scre->root);\n\txcb_query_pointer_reply_t *poin = xcb_query_pointer_reply(dpy, coord, 0);\n\tif ((values[2] == (uint32_t)(1)) && (win != 0)) {\n\t\txcb_get_geometry_cookie_t geom_now = xcb_get_geometry(dpy, win);\n\t\txcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(dpy, geom_now, NULL);\n\t\tuint16_t geom_x = geom->width + (2 * BORDER_WIDTH);\n\t\tuint16_t geom_y = geom->height + (2 * BORDER_WIDTH);\n\t\tvalues[0] = ((poin->root_x + geom_x) > scre->width_in_pixels) ?\n\t\t\t(scre->width_in_pixels - geom_x) : poin->root_x;\n\t\tvalues[1] = ((poin->root_y + geom_y) > scre->height_in_pixels) ?\n\t\t\t(scre->height_in_pixels - geom_y) : poin->root_y;\n\t\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X\n\t\t\t| XCB_CONFIG_WINDOW_Y, values);\n\t} else if ((values[2] == (uint32_t)(3)) && (win != 0)) {\n\t\txcb_get_geometry_cookie_t geom_now = xcb_get_geometry(dpy, win);\n\t\txcb_get_geometry_reply_t* geom = xcb_get_geometry_reply(dpy, geom_now, NULL);\n\t\tif (!((poin->root_x <= geom->x) || (poin->root_y <= geom->y))) {\n\t\t\tvalues[0] = poin->root_x - geom->x - BORDER_WIDTH;\n\t\t\tvalues[1] = poin->root_y - geom->y - BORDER_WIDTH;\n\t\t\tif ((values[0] >= (uint32_t)(WINDOW_MIN_WIDTH)) &&\n\t\t\t\t(values[1] >= (uint32_t)(WINDOW_MIN_HEIGHT))) {\n\t\t\t\txcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH\n\t\t\t\t\t| XCB_CONFIG_WINDOW_HEIGHT, values);\n\t\t\t}\n\t\t}\n\t} else {}\n}\n\nstatic xcb_keycode_t *xcb_get_keycodes(xcb_keysym_t keysym) {\n\txcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(dpy);\n\txcb_keycode_t *keycode;\n\tkeycode = (!(keysyms) ? NULL : xcb_key_symbols_get_keycode(keysyms, keysym));\n\txcb_key_symbols_free(keysyms);\n\treturn keycode;\n}\n\nstatic xcb_keysym_t xcb_get_keysym(xcb_keycode_t keycode) {\n\txcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(dpy);\n\txcb_keysym_t keysym;\n\tkeysym = (!(keysyms) ? 0 : xcb_key_symbols_get_keysym(keysyms, keycode, 0));\n\txcb_key_symbols_free(keysyms);\n\treturn keysym;\n}\n\nstatic void setFocus(xcb_drawable_t window) {\n\tif ((window != 0) && (window != scre->root)) {\n\t\txcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, window,\n\t\t\tXCB_CURRENT_TIME);\n\t}\n}\n\nstatic void setFocusColor(xcb_window_t window, int focus) {\n\tif ((BORDER_WIDTH > 0) && (scre->root != window) && (0 != window)) {\n\t\tuint32_t vals[1];\n\t\tvals[0] = focus ? BORDER_COLOR_FOCUSED : BORDER_COLOR_UNFOCUSED;\n\t\txcb_change_window_attributes(dpy, window, XCB_CW_BORDER_PIXEL, vals);\n\t\txcb_flush(dpy);\n\t}\n}\n\nstatic void handleKeyPress(xcb_generic_event_t *ev) {\n\txcb_key_press_event_t *e = ( xcb_key_press_event_t *) ev;\n\txcb_keysym_t keysym = xcb_get_keysym(e->detail);\n\twin = e->child;\n\tint key_table_size = sizeof(keys) / sizeof(*keys);\n\tfor (int i = 0; i < key_table_size; ++i) {\n\t\tif ((keys[i].keysym == keysym) && (keys[i].mod == e->state)) {\n\t\t\tkeys[i].func(keys[i].com);\n\t\t}\n\t}\n}\n\nstatic void handleEnterNotify(xcb_generic_event_t *ev) {\n\txcb_enter_notify_event_t *e = ( xcb_enter_notify_event_t *) ev;\n\tsetFocus(e->event);\n}\n\nstatic void handleButtonRelease(xcb_generic_event_t *ev) {\n\tUNUSED(ev);\n\txcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);\n}\n\nstatic void handleDestroyNotify(xcb_generic_event_t *ev) {\n\txcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) ev;\n\txcb_kill_client(dpy, e->window);\n}\n\nstatic void handleFocusIn(xcb_generic_event_t *ev) {\n\txcb_focus_in_event_t *e = (xcb_focus_in_event_t *) ev;\n\tsetFocusColor(e->event, 1);\n}\n\nstatic void handleFocusOut(xcb_generic_event_t *ev) {\n\txcb_focus_out_event_t *e = (xcb_focus_out_event_t *) ev;\n\tsetFocusColor(e->event, 0);\n}\n\nstatic void handleMapRequest(xcb_generic_event_t *ev) {\n\txcb_map_request_event_t *e = (xcb_map_request_event_t *) ev;\n\txcb_map_window(dpy, e->window);\n\tuint32_t vals[5];\n\tvals[0] = (scre->width_in_pixels / 2) - (WINDOW_WIDTH / 2);\n\tvals[1] = (scre->height_in_pixels / 2) - (WINDOW_HEIGHT / 2);\n\tvals[2] = WINDOW_WIDTH;\n\tvals[3] = WINDOW_HEIGHT;\n\tvals[4] = BORDER_WIDTH;\n\txcb_configure_window(dpy, e->window, XCB_CONFIG_WINDOW_X |\n\t\tXCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH |\n\t\tXCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH, vals);\n\txcb_flush(dpy);\n\tvalues[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE;\n\txcb_change_window_attributes_checked(dpy, e->window,\n\t\tXCB_CW_EVENT_MASK, values);\n\tsetFocus(e->window);\n}\n\nstatic int eventHandler(void) {\n\tint ret = xcb_connection_has_error(dpy);\n\tif (ret == 0) {\n\t\txcb_generic_event_t *ev = xcb_wait_for_event(dpy);\n\t\tif (ev != NULL) {\n\t\t\thandler_func_t *handler;\n\t\t\tfor (handler = handler_funs; handler->func != NULL; handler++) {\n\t\t\t\tif ((ev->response_type & ~0x80) == handler->request) {\n\t\t\t\t\thandler->func(ev);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfree(ev);\n\t\t}\n\t}\n\txcb_flush(dpy);\n\treturn ret;\n}\n\nstatic void setup(void) {\n\tvalues[0] = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT\n\t\t| XCB_EVENT_MASK_STRUCTURE_NOTIFY\n\t\t| XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY\n\t\t| XCB_EVENT_MASK_PROPERTY_CHANGE;\n\txcb_change_window_attributes_checked(dpy, scre->root,\n\t\tXCB_CW_EVENT_MASK, values);\n\txcb_ungrab_key(dpy, XCB_GRAB_ANY, scre->root, XCB_MOD_MASK_ANY);\n\tint key_table_size = sizeof(keys) / sizeof(*keys);\n\tfor (int i = 0; i < key_table_size; ++i) {\n\t\txcb_keycode_t *keycode = xcb_get_keycodes(keys[i].keysym);\n\t\tif (keycode != NULL) {\n\t\t\txcb_grab_key(dpy, 1, scre->root, keys[i].mod, *keycode,\n\t\t\t\tXCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC );\n\t\t}\n\t}\n\txcb_flush(dpy);\n\txcb_grab_button(dpy, 0, scre->root, XCB_EVENT_MASK_BUTTON_PRESS |\n\t\tXCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC,\n\t\tXCB_GRAB_MODE_ASYNC, scre->root, XCB_NONE, 1, MOD1);\n\txcb_grab_button(dpy, 0, scre->root, XCB_EVENT_MASK_BUTTON_PRESS |\n\t\tXCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC,\n\t\tXCB_GRAB_MODE_ASYNC, scre->root, XCB_NONE, 3, MOD1);\n\txcb_flush(dpy);\n}\n\nstatic int die(char *errstr) {\n\tsize_t n = 0;\n\tchar *p = errstr;\n\twhile ((* (p++)) != 0) {\n\t\t++n;\n\t}\n\tssize_t o = write(STDERR_FILENO, errstr, n);\n\tint ret = 1;\n\tif (o < 0) {\n\t\tret = -1;\n\t}\n\treturn ret;\n}\n\nstatic int strcmp_c(char *str1, char *str2) {\n\tchar *c1 = str1;\n\tchar *c2 = str2;\n\twhile ((* c1) && ((* c1) == (* c2))) {\n\t\t++c1;\n\t\t++c2;\n\t}\n\tint n = (* c1) - (* c2);\n\treturn n;\n}\n\nint main(int argc, char *argv[]) {\n\tint ret = 0;\n\tif ((argc == 2) && (strcmp_c(\"-v\", argv[1]) == 0)) {\n\t\tret = die(\"xwm-0.1.9, Copyright © 2021-2022 Michael Czigler, MIT License\\n\");\n\t}\n\tif ((ret == 0) && (argc != 1)) {\n\t\tret = die(\"usage: xwm [-v]\\n\");\n\t}\n\tif (ret == 0) {\n\t\tdpy = xcb_connect(NULL, NULL);\n\t\tret = xcb_connection_has_error(dpy);\n\t\tif (ret > 0) {\n\t\t\tret = die(\"xcb_connection_has_error\\n\");\n\t\t}\n\t}\n\tif (ret == 0) {\n\t\tscre = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;\n\t\tsetup();\n\t}\n\twhile (ret == 0) {\n\t\tret = eventHandler();\n\t}\n\treturn ret;\n}\n"
  },
  {
    "path": "xwm.h",
    "content": "/* See LICENSE file for copyright and license details. */\n\n/* typedefs */\ntypedef struct {\n    unsigned int mod;\n    xcb_keysym_t keysym;\n    void (*func)(char **com);\n    char **com;\n} Key;\n\ntypedef struct {\n    uint32_t request;\n    void (*func)(xcb_generic_event_t *ev);\n} handler_func_t;\n\n/* convert keycode to keysym and back */\nstatic xcb_keycode_t *xcb_get_keycodes(xcb_keysym_t keysym);\nstatic xcb_keysym_t    xcb_get_keysym(xcb_keycode_t keycode);\n\n/* user defined command actions */\nstatic void killclient(char **com);\nstatic void spawn(char **com);\nstatic void closewm(char **com);\nstatic void fullclient(char **com);\n\n/* window behavior */\nstatic void setFocus(xcb_drawable_t window);\nstatic void setFocusColor(xcb_drawable_t window, int focus);\n\n/* event hander actions */\nstatic int eventHandler(void);\nstatic void handleMotionNotify(xcb_generic_event_t *ev);\nstatic void handleEnterNotify(xcb_generic_event_t *ev);\nstatic void handleDestroyNotify(xcb_generic_event_t *ev);\nstatic void handleButtonPress(xcb_generic_event_t *ev);\nstatic void handleButtonRelease(xcb_generic_event_t *ev);\nstatic void handleKeyPress(xcb_generic_event_t *ev);\nstatic void handleMapRequest(xcb_generic_event_t *ev);\nstatic void handleFocusIn(xcb_generic_event_t *ev);\nstatic void handleFocusOut(xcb_generic_event_t *ev);\nstatic handler_func_t handler_funs[] = {\n    { XCB_MOTION_NOTIFY,  handleMotionNotify },\n    { XCB_ENTER_NOTIFY,   handleEnterNotify },\n    { XCB_DESTROY_NOTIFY, handleDestroyNotify },\n    { XCB_BUTTON_PRESS,   handleButtonPress },\n    { XCB_BUTTON_RELEASE, handleButtonRelease },\n    { XCB_KEY_PRESS,      handleKeyPress },\n    { XCB_MAP_REQUEST,    handleMapRequest },\n    { XCB_FOCUS_IN,       handleFocusIn },\n    { XCB_FOCUS_OUT,      handleFocusOut },\n    { XCB_NONE,           NULL }\n};\n\n/* intialize */\nstatic void setup(void);\n\n/* error handling & misc. */\nstatic int die(char *errstr);\nstatic int strcmp_c(char *str1, char *str2);\n"
  }
]