commit ea5c31b01e93542298adef1b487dd46cd58cccee
parent 0ca4b7d43224a7c861e230da0b164625446cd02a
Author: Kebigon <git@kebigon.xyz>
Date: Mon, 6 Jul 2020 20:39:33 +0900
Move/resize windows
Diffstat:
| M | config.h | | | 35 | +++++++++++++++++++++++++++++++---- |
| M | kbgwm.c | | | 276 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ |
| M | types.h | | | 44 | ++++++++++++++++++++++++++++++++++++++------ |
| M | xcbutils.c | | | 141 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------- |
| M | xcbutils.h | | | 36 | +++++++++++++++++++++++++++++++----- |
5 files changed, 469 insertions(+), 63 deletions(-)
diff --git a/config.h b/config.h
@@ -1,9 +1,35 @@
-
#define MODKEY XCB_MOD_MASK_4
-static const char* termcmd[] = {"alacritty", NULL};
+#define FOCUS_COLOR 0xFF0000
+#define UNFOCUS_COLOR 0x005577
+
+#define BORDER_WIDTH 1
+
+static const char* termcmd [] = { "alacritty",
+ NULL };
+static const char* menucmd [] = { "dmenu_run",
+ NULL };
-Key keys[] =
+static Key keys [] =
{
- { MODKEY, XK_Return, start, { .cmd = termcmd } }
+ { MODKEY,
+ XK_Return,
+ start,
+ { .cmd = termcmd }},
+ { MODKEY,
+ XK_p,
+ start,
+ { .cmd = menucmd }}
};
+
+static Button buttons [] =
+{
+ { MODKEY,
+ XCB_BUTTON_INDEX_1,
+ mousemove,
+ { 0 }},
+ { MODKEY,
+ XCB_BUTTON_INDEX_3,
+ mouseresize,
+ { 0 }},
+};
+\ No newline at end of file
diff --git a/kbgwm.c b/kbgwm.c
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
+#include <assert.h>
#include <xcb/xcb.h>
#include <X11/keysym.h>
@@ -9,37 +10,92 @@
#include "xcbutils.h"
#define IGNORE_LOCK(modifier) (modifier & ~(XCB_MOD_MASK_LOCK))
+#define MATCH_MODIFIERS(expected, actual) ((actual & expected) == expected)
static void start(const Arg* arg);
+static void mousemove(const Arg* arg);
+static void mouseresize(const Arg* arg);
#include "config.h"
+#define BORDER_WIDTH_X2 (BORDER_WIDTH << 1)
+
bool quit = false;
+bool moving = false;
+bool resizing = false;
xcb_connection_t* c;
xcb_window_t root;
+xcb_screen_t* screen;
+uint_least16_t previous_x;
+uint_least16_t previous_y;
-void setupEvents()
+static window* focusedWindow;
+window* windows;
+
+window* findWindow(xcb_window_t id)
{
- /*
- * Get all the key symbols
- */
+ window* window = windows;
-// xcb_grab_key(c, 1, root, XCB_MOD_MASK_ANY, XCB_GRAB_ANY,
-// XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ do
+ {
+ if (window->id == id)
+ return (window);
+ }
+ while ((window = window->next) != NULL);
- xcb_grab_button(c, 0, root, XCB_EVENT_MASK_BUTTON_PRESS |
- XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC,
- XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 1, XCB_MOD_MASK_ANY);
+ return NULL;
+}
- xcb_grab_button(c, 0, root, XCB_EVENT_MASK_BUTTON_PRESS |
- XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC,
- XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 3, XCB_MOD_MASK_ANY);
+void focus(xcb_window_t id)
+{
+ printf("focus: id=%d\n", id);
- for (uint_fast8_t i = 0; i != LENGTH(keys); i++)
+ uint32_t values[1];
+
+ window* window = findWindow(id);
+ assert(window != NULL);
+
+ // A window was previously focused -> we change its color
+ if (focusedWindow != NULL)
{
- xcb_register_key_press_events(keys[i]);
+ xcb_change_window_attributes(c, focusedWindow->id, XCB_CW_BORDER_PIXEL, (uint32_t[]) { 0xFF000000 | UNFOCUS_COLOR });
}
+ xcb_change_window_attributes(c, id, XCB_CW_BORDER_PIXEL, (uint32_t[]) { 0xFF000000 | FOCUS_COLOR });
+
+ // Raise the window so it is on top
+ values[0] = XCB_STACK_MODE_TOP_IF;
+ xcb_configure_window(c, id, XCB_CONFIG_WINDOW_STACK_MODE, values);
+
+ // Set the keyboard on the focused window
+ xcb_set_input_focus(c, XCB_NONE, id, XCB_CURRENT_TIME);
+ xcb_flush(c);
+
+ focusedWindow = window;
+ printf("focus set to: id=%d\n", focusedWindow->id);
+}
+
+void setupEvents()
+{
+ uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY };
+
+ xcb_change_window_attributes_checked(c, root, XCB_CW_EVENT_MASK, values);
+
+ xcb_grab_button(c, 1, root, XCB_EVENT_MASK_BUTTON_PRESS,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root,
+ XCB_NONE, XCB_BUTTON_INDEX_1, 0 | XCB_MOD_MASK_2);
+ xcb_grab_button(c, 1, root, XCB_EVENT_MASK_BUTTON_PRESS,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root,
+ XCB_NONE, XCB_BUTTON_INDEX_3, 0 | XCB_MOD_MASK_2);
+
+ for (uint_fast8_t i = 0; i != LENGTH(keys); i++)
+ xcb_register_key_events(keys[i]);
+
+ for (uint_fast8_t i = 0; i != LENGTH(buttons); i++)
+ xcb_register_button_events(buttons[i]);
+
xcb_flush(c);
}
@@ -59,9 +115,10 @@ void handle_keypress(xcb_key_press_event_t* event)
for (uint_fast8_t i = 0; i < LENGTH(keys); i++)
{
- printf("testing key: mod %d key %d\n", keys[i].modifiers, keys[i].keysym);
+ printf("testing key: mod %d key %d\n", keys[i].modifiers,
+ keys[i].keysym);
- if (keysym == keys[i].keysym && (event->state & keys[i].modifiers) == keys[i].modifiers)
+ if (keysym == keys[i].keysym && MATCH_MODIFIERS(keys[i].modifiers, event->state))
{
printf("Key found !\n");
keys[i].func(&keys[i].arg);
@@ -70,6 +127,148 @@ void handle_keypress(xcb_key_press_event_t* event)
}
}
+void handle_maprequest(xcb_map_request_event_t* event)
+{
+ xcb_get_geometry_reply_t* geometry;
+
+ printf("received map request: parent %d xcb_window_t %d\n", event->parent, event->window);
+
+ geometry = xcb_get_geometry_reply(c, xcb_get_geometry(c, event->window), NULL);
+
+ window* window = emalloc(sizeof window);
+ window->id = event->window;
+ window->x = geometry->x;
+ window->y = geometry->y;
+ window->width = geometry->width;
+ window->height = geometry->height;
+
+ if (windows != NULL)
+ {
+ window->next = windows;
+ }
+
+ windows = window;
+
+ printf("new window: id=%d x=%d y=%d width=%d height=%d\n", window->id, window->x, window->y, window->width, window->height);
+
+ focus(event->window);
+
+ xcb_configure_window(c, window->id, XCB_CONFIG_WINDOW_BORDER_WIDTH, (uint32_t[]) { BORDER_WIDTH });
+
+ // Display the window
+ xcb_map_window(c, window->id);
+ xcb_flush(c);
+}
+
+void handle_buttonpress(xcb_button_press_event_t* event)
+{
+ printf("handle_buttonpress: child=%d\n", event->child);
+
+ // Click on the root window -> ignore
+ if (event->child == 0)
+ return;
+
+ // Focus window if needed
+ if (event->child != focusedWindow->id)
+ focus(event->child);
+
+ for (uint_fast8_t i = 0; i < LENGTH(buttons); i++)
+ {
+ if (event->detail == buttons[i].keysym && MATCH_MODIFIERS(buttons[i].modifiers,event->state))
+ {
+ previous_x = event->root_x;
+ previous_y = event->root_y;
+
+ printf("Button found !\n");
+ buttons[i].func(&buttons[i].arg);
+ break;
+ }
+ }
+}
+
+void handle_buttonrelease(xcb_button_release_event_t* event)
+{
+ assert(moving || resizing);
+
+ printf("handle_buttonrelease: mod=%d button=%d\n", event->state, event->detail);
+
+ xcb_ungrab_pointer(c, XCB_CURRENT_TIME);
+ xcb_flush(c);
+
+ moving = false;
+ resizing = false;
+}
+
+void mousemove(const Arg* arg)
+{
+ printf("mousemove\n");
+ moving = true;
+
+ xcb_grab_pointer(c, 0, screen->root,
+ XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root,
+ XCB_NONE,
+ XCB_CURRENT_TIME);
+ xcb_flush(c);
+}
+
+void mouseresize(const Arg* arg)
+{
+ printf("mouseresize\n");
+ resizing = true;
+
+ xcb_grab_pointer(c, 0, screen->root,
+ XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root,
+ XCB_NONE,
+ XCB_CURRENT_TIME);
+ xcb_flush(c);
+}
+
+void handle_motionnotify(xcb_motion_notify_event_t* event)
+{
+ assert(moving || resizing);
+ assert(focusedWindow != NULL);
+ assert(focusedWindow->id != root);
+
+ printf("handle_motionnotify: root_x=%d root_y=%d event_x=%d event_y=%d\n",
+ event->root_x, event->root_y, event->event_x, event->event_y);
+
+ uint16_t diff_x = event->root_x - previous_x;
+ uint16_t diff_y = event->root_y - previous_y;
+ previous_x = event->root_x;
+ previous_y = event->root_y;
+
+ if (moving)
+ {
+ focusedWindow->x += diff_x;
+ focusedWindow->y += diff_y;
+
+ if (focusedWindow->x < 0)
+ focusedWindow->x = 0;
+ else if (focusedWindow->x + focusedWindow->width + BORDER_WIDTH_X2 > screen->width_in_pixels)
+ focusedWindow->x = screen->width_in_pixels - focusedWindow->width - BORDER_WIDTH_X2;
+
+ if (focusedWindow->y < 0)
+ focusedWindow->y = 0;
+ else if (focusedWindow->y + focusedWindow->height + BORDER_WIDTH_X2 > screen->height_in_pixels)
+ focusedWindow->y = screen->height_in_pixels - focusedWindow->height - BORDER_WIDTH_X2;
+
+ uint32_t values[2] = { focusedWindow->x, focusedWindow->y };
+ xcb_configure_window(c, focusedWindow->id, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values);
+ }
+ else if (resizing)
+ {
+ focusedWindow->width += diff_x;
+ focusedWindow->height += diff_y;
+
+ uint32_t values[2] = { focusedWindow->width, focusedWindow->height };
+ xcb_configure_window(c, focusedWindow->id, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
+ }
+
+ xcb_flush(c);
+}
+
void eventLoop()
{
xcb_generic_event_t* event;
@@ -85,13 +284,34 @@ void eventLoop()
handle_keypress((xcb_key_press_event_t*) event);
break;
+ case XCB_BUTTON_PRESS:
+ printf("\nXCB_BUTTON_PRESS\n");
+ handle_buttonpress((xcb_button_press_event_t*) event);
+ break;
+
+ case XCB_BUTTON_RELEASE:
+ printf("\nXCB_BUTTON_RELEASE\n");
+ handle_buttonrelease((xcb_button_release_event_t*) event);
+ break;
+
+ case XCB_MAP_REQUEST:
+ printf("\nXCB_MAP_REQUEST\n");
+ handle_maprequest((xcb_map_request_event_t*) event);
+ break;
+
case XCB_MAPPING_NOTIFY:
printf("\nXCB_MAPPING_NOTIFY\n");
onMappingNotify((xcb_mapping_notify_event_t*) event);
break;
+ case XCB_MOTION_NOTIFY:
+ printf("\nXCB_MOTION_NOTIFY\n");
+ handle_motionnotify((xcb_motion_notify_event_t*) event);
+ break;
+
default:
- printf("Received event, response type %d\n", event->response_type & ~0x80);
+ printf("Received event, response type %d\n",
+ event->response_type & ~0x80);
break;
}
@@ -101,11 +321,18 @@ void eventLoop()
void start(const Arg* arg)
{
+ printf("start %s\n", arg->cmd[0]);
+
if (fork() == 0)
{
// Child process
setsid();
- execvp((char*)arg->cmd[0], (char**)arg->cmd);
+
+ if (execvp((char*) arg->cmd[0], (char**) arg->cmd) == -1)
+ {
+ perror(arg->cmd[0]);
+ exit(-1);
+ }
}
}
@@ -115,7 +342,9 @@ int main(void)
* displayname = NULL -> use DISPLAY environment variable
*/
int screenp;
- c = xcb_connect(NULL, &screenp);
+ c = xcb_connect(
+ NULL, &screenp);
+
if (xcb_connection_has_error(c))
{
perror("xcb_connect");
@@ -126,12 +355,14 @@ int main(void)
* Find the screen
*/
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(c));
+
for (int i = 0; i < screenp; ++i)
{
xcb_screen_next(&iter);
}
- xcb_screen_t* screen = iter.data;
+ screen = iter.data;
+
if (!screen)
{
xcb_disconnect(c);
@@ -141,6 +372,9 @@ int main(void)
root = screen->root;
+ printf("White: %d\n", screen->white_pixel);
+ printf("Black: %d\n", screen->black_pixel);
+
xcb_flush(c);
setupEvents();
@@ -150,5 +384,5 @@ int main(void)
xcb_disconnect(c);
- return 0;
+ return (0);
}
diff --git a/types.h b/types.h
@@ -1,15 +1,46 @@
#ifndef TYPES_H_
#define TYPES_H_
-typedef union {
+typedef union
+{
+
const char** cmd;
-} Arg;
+}
+Arg;
+
+typedef struct
+{
+ uint16_t modifiers;
+ xcb_keysym_t keysym;
+ void (*func)
+ (
+ const Arg*
+ );
+ const Arg arg;
+}
+Key;
-typedef struct {
+typedef struct
+{
uint16_t modifiers;
xcb_keysym_t keysym;
- void (* func)(const Arg*);
+ void (*func)
+ (
+ const Arg*
+ );
const Arg arg;
-} Key;
+}
+Button;
+
+typedef struct window window;
+struct window
+{
+ xcb_window_t id;
+ int_least16_t x,
+ y;
+ uint_least16_t width,
+ height;
+ window* next;
+};
-#endif /* TYPES_H_ */
+#endif /* TYPES_H_ */
+\ No newline at end of file
diff --git a/xcbutils.c b/xcbutils.c
@@ -6,57 +6,143 @@
extern xcb_connection_t* c;
extern xcb_window_t root;
+void* emalloc
+(
+ size_t size
+)
+{
+ void* p;
+
+ if ( !(p = malloc (
+ size ))
+ )
+ {
+ printf (
+ "Out of memory" );
+ exit (
+ -1 );
+ }
+
+ return
+ (p);
+}
+
/*
*
*/
-void xcb_register_key_press_events(Key key)
+// TODO: understand why XCB_MOD_MASK_2 is always present
+#define IGNORED_MODIFIERS XCB_MOD_MASK_2
+
+void xcb_register_key_events
+(
+ Key key
+)
{
xcb_keycode_t* keycodes;
xcb_keycode_t keycode;
- printf("Registering key press event for key %d / key %d\n", key.modifiers, key.keysym);
-
- keycodes = xcb_get_keycodes(key.keysym);
-
- for (int i = 0; (keycode = keycodes[i]) != XCB_NO_SYMBOL; i++)
- xcb_grab_key(c, 1, root, key.modifiers, keycode,
- XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ printf (
+ "Registering key press event for key %d / key %d\n",
+ key.modifiers,
+ key.keysym );
+
+ keycodes = xcb_get_keycodes (
+ key.keysym );
+
+ for ( int i = 0; (keycode = keycodes [ i ]) != XCB_NO_SYMBOL; i++ )
+ {
+ xcb_grab_key (
+ c,
+ 1,
+ root,
+ key.modifiers | IGNORED_MODIFIERS,
+ keycode,
+ XCB_GRAB_MODE_ASYNC,
+ XCB_GRAB_MODE_ASYNC );
+ }
+
+ free (
+ keycodes );
+}
- free(keycodes);
+void xcb_register_button_events
+(
+ Button button
+)
+{
+ xcb_grab_button (
+ c,
+ 0,
+ root,
+ BUTTON_EVENT_MASK,
+ XCB_GRAB_MODE_ASYNC,
+ XCB_GRAB_MODE_ASYNC,
+ root,
+ XCB_NONE,
+ button.keysym,
+ button.modifiers | IGNORED_MODIFIERS );
}
/*
* Get keycodes from a keysym
* TODO: check if there's a way to keep keysyms
*/
-xcb_keycode_t* xcb_get_keycodes(xcb_keysym_t keysym)
+xcb_keycode_t* xcb_get_keycodes
+(
+ xcb_keysym_t keysym
+)
{
xcb_key_symbols_t* keysyms;
- // Not able to retrieve the keysyms somehow
- if (!(keysyms = xcb_key_symbols_alloc(c)))
- return NULL;
-
- xcb_keycode_t* keycode = xcb_key_symbols_get_keycode(keysyms, keysym);
-
- xcb_key_symbols_free(keysyms);
- return keycode;
+ if ( !(keysyms = xcb_key_symbols_alloc (
+ c ))
+ )
+ {
+ perror (
+ "Not able to retrieve symbols" );
+ return
+ (NULL);
+ }
+
+ xcb_keycode_t* keycode = xcb_key_symbols_get_keycode (
+ keysyms,
+ keysym );
+
+ xcb_key_symbols_free (
+ keysyms );
+ return
+ (keycode);
}
/*
* Get keysym from a keycode
* TODO: check if there's a way to keep keysyms
*/
-xcb_keysym_t xcb_get_keysym(xcb_keycode_t keycode)
+xcb_keysym_t xcb_get_keysym
+(
+ xcb_keycode_t keycode
+)
{
xcb_key_symbols_t* keysyms;
- if (!(keysyms = xcb_key_symbols_alloc(c)))
- return 0;
-
- xcb_keysym_t keysym = xcb_key_symbols_get_keysym(keysyms, keycode, 0);
-
- xcb_key_symbols_free(keysyms);
- return keysym;
-}
+ if ( !(keysyms = xcb_key_symbols_alloc (
+ c ))
+ )
+ {
+ perror (
+ "Not able to retrieve symbols" );
+ return
+ (0);
+ }
+
+ xcb_keysym_t keysym = xcb_key_symbols_get_keysym (
+ keysyms,
+ keycode,
+ 0 );
+
+ xcb_key_symbols_free (
+ keysyms );
+ return
+ (keysym);
+}
+\ No newline at end of file
diff --git a/xcbutils.h b/xcbutils.h
@@ -1,19 +1,44 @@
#ifndef XCBUTILS_H_
#define XCBUTILS_H_
-#define LENGTH(X) (sizeof X / sizeof X[0])
+#define LENGTH( X ) (sizeof X / sizeof X [ 0 ])
#include <xcb/xcb_keysyms.h>
#include <xcb/xcb.h>
#include "types.h"
+#define BUTTON_EVENT_MASK XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
+
+void*
+emalloc
+(
+ size_t size
+);
+
/*
* registering events
*/
-void xcb_register_key_press_events(Key key);
+void
+xcb_register_key_events
+(
+ Key key
+);
+void
+xcb_register_button_events
+(
+ Button button
+);
-xcb_keycode_t* xcb_get_keycodes(xcb_keysym_t);
-xcb_keysym_t xcb_get_keysym(xcb_keycode_t);
+xcb_keycode_t*
+xcb_get_keycodes
+(
+ xcb_keysym_t
+);
+xcb_keysym_t
+xcb_get_keysym
+(
+ xcb_keycode_t
+);
-#endif /* XCBUTILS_H_ */
+#endif /* XCBUTILS_H_ */
+\ No newline at end of file