aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-07-27 19:09:09 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-27 19:09:09 +0200
commit1212214f8af6dfa7b16986d812bcec4ccaf92638 (patch)
tree137cc8554780ae681b3094a9efca609fc364f7f5
parent6411ee34ad3efbad8665047f3593fe9426ca7722 (diff)
wip: scale monitor instead of faking mouse input to move mouse outside the monitor
-rw-r--r--README.md4
-rw-r--r--include/window_manager.h17
-rw-r--r--project.conf4
-rw-r--r--src/main.cpp215
-rw-r--r--src/window_manager.c395
5 files changed, 357 insertions, 278 deletions
diff --git a/README.md b/README.md
index 8f8445e..0b2c8e4 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ The `export QT_AUTO_SCREEN_SCALE_FACTOR=1` line is to make qt applications follo
# Keys
Alt+F1 = reset camera\
Alt+F12 = Open rofi application start menu\
+Alt+Q = Close the focused window\
Alt+Mouse move = Move the view left/right
# Known issues
@@ -32,4 +33,5 @@ Keyboard input doesn't always work for all windows...
# TODO
Show the window manager inside games by creating the vr window manager as an openvr overlay. The overlay texture will be automatically
displayed inside SteamVR in the hmd when displaying settings (pressing the button on the hmd).\
-Add camera culling.
+Add camera culling.\
+Automatically set `Xft.dpi: 240` at runtime.
diff --git a/include/window_manager.h b/include/window_manager.h
index d2672c0..86f4f46 100644
--- a/include/window_manager.h
+++ b/include/window_manager.h
@@ -4,18 +4,26 @@
#include <X11/Xlib.h>
#include <pthread.h>
-/* TODO: Make this dynamic */
+/* TODO: Use a hash map instead */
#define WINDOW_MANAGER_MAX_WINDOWS 128
typedef struct {
Window window;
+ int visible;
} WindowManagerWindowData;
+typedef enum {
+ CURSOR_WRAP_NONE,
+ CURSOR_WRAP_LEFT,
+ CURSOR_WRAP_RIGHT
+} CursorWrapSide;
+
typedef void (*WindowManagerAddWindowCallback)(Window window, void *userdata);
-typedef void (*WindowManagerRemoveWindowCallback)(Window window, void *userdata);
+/* Return the new focused window. All other windows will be hidden to not interfer with input (but they will still be visible in vr) */
+typedef Window (*WindowManagerRemoveWindowCallback)(Window window, void *userdata);
typedef void (*WindowManagerResetCameraCallback)(void *userdata);
-typedef void (*WindowManagerMoveCursorCallback)(Display *display, int diff_x, int diff_y, void *userdata);
-typedef void (*WindowManagerKeyCallback)(Display *display, int type, unsigned int state, unsigned int keycode, void *userdata);
+/* Return the new focused window. All other windows will be hidden to not interfer with input (but they will still be visible in vr) */
+typedef Window (*WindowManagerMoveCursorCallback)(Display *display, int cursor_x, int cursor_y, CursorWrapSide cursor_wrap_side, void *userdata);
typedef void (*WindowManagerButtonCallback)(Display *display, int type, unsigned int state, unsigned int button, void *userdata);
typedef struct {
@@ -23,7 +31,6 @@ typedef struct {
WindowManagerRemoveWindowCallback remove_window_callback;
WindowManagerResetCameraCallback reset_camera_callback;
WindowManagerMoveCursorCallback move_cursor_callback;
- WindowManagerKeyCallback key_callback;
WindowManagerButtonCallback button_callback;
} WindowManagerCallback;
diff --git a/project.conf b/project.conf
index 397dfc5..264e099 100644
--- a/project.conf
+++ b/project.conf
@@ -11,4 +11,6 @@ glm = "0"
glew = "2"
sdl2 = "2"
#glfw3 = "3"
-openvr = "1" \ No newline at end of file
+openvr = "1"
+xrandr = "1"
+xi = "1" \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 9b7968a..40967e8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -126,12 +126,12 @@ public:
bool CreateAllShaders();
bool AddWindow(Window window);
- bool RemoveWindow(Display *display, Window window);
+ bool RemoveWindow(Window window);
void AddPendingWindowEvent(Window window, PendingWindowEventType event_type);
- void SetFocusedWindowFromMousePosition(Display *display);
+ void SetFocusedWindowFromMousePosition();
+ Window GetFocusedWindowFromMousePosition(Window exclude_window);
- void MoveCursor(Display *display, int diff_x, int diff_y);
- void KeyCallback(Display *display, int type, unsigned int state, unsigned int keycode);
+ Window MoveCursor(Display *display, int diff_x, int diff_y, CursorWrapSide cursor_wrap_side);
void ButtonCallback(Display *display, int type, unsigned int state, unsigned int button);
void GetMonitorRelativeMousePosition(int *x, int *y);
@@ -263,6 +263,7 @@ private: // X compositor
};
std::vector<std::unique_ptr<WindowData>> window_data_list;
+ std::atomic_int num_windows;
Window focused_window = None;
int focused_window_offset_x = 0;
int focused_window_offset_y = 0;
@@ -420,6 +421,7 @@ bool CMainApplication::AddWindow(Window window) {
XSelectInput(x_display, window_data->src_window_id, StructureNotifyMask);
window_data_list.push_back(std::move(window_data));
+ num_windows++;
fprintf(stderr, "added window: %ld\n", window);
fflush(stderr);
@@ -427,37 +429,7 @@ bool CMainApplication::AddWindow(Window window) {
return true;
}
-static void SendEnterLeaveNotifyEvent(Display *display, Window target_window, int x, int y, int type) {
- assert(type == EnterNotify || type == LeaveNotify);
-
- XEvent fake_event;
- memset(&fake_event, 0, sizeof(fake_event));
- fake_event.type = type;
- fake_event.xcrossing.type = type;
- fake_event.xcrossing.window = target_window;
- fake_event.xcrossing.root = DefaultRootWindow(display);
- fake_event.xcrossing.subwindow = target_window;
- fake_event.xcrossing.time = CurrentTime;
- fake_event.xcrossing.x = x;
- fake_event.xcrossing.y = y;
- fake_event.xcrossing.mode = NotifyNormal;
- if(XSendEvent(display, target_window, True, 0xfff, &fake_event) == 0)
- fprintf(stderr, "fake enter/leave notify event failed!\n");
-
- XEvent fake_focus_event;
- memset(&fake_focus_event, 0, sizeof(fake_focus_event));
- fake_focus_event.type = (type == EnterNotify ? FocusIn : FocusOut);
- fake_focus_event.xfocus.type = fake_focus_event.type;
- fake_focus_event.xfocus.window = target_window;
- fake_focus_event.xfocus.mode = NotifyNormal;
- if(XSendEvent(display, target_window, True, 0xfff, &fake_focus_event) == 0)
- fprintf(stderr, "fake focus change event failed!\n");
-
- /* TODO: Remove flush? */
- XFlush(display);
-}
-
-void CMainApplication::SetFocusedWindowFromMousePosition(Display *display) {
+void CMainApplication::SetFocusedWindowFromMousePosition() {
int mx = mouse_x;
int my = mouse_y;
@@ -470,43 +442,46 @@ void CMainApplication::SetFocusedWindowFromMousePosition(Display *display) {
if(prev_window == window_data->src_window_id)
return;
- if(prev_window) {
- int x;
- int y;
- GetMonitorRelativeMousePosition(&x, &y);
- SendEnterLeaveNotifyEvent(display, prev_window, x, y, LeaveNotify);
- }
-
focused_window = window_data->src_window_id;
focused_window_offset_x = x;
focused_window_offset_y = 0;
-
- int x;
- int y;
- GetMonitorRelativeMousePosition(&x, &y);
- SendEnterLeaveNotifyEvent(display, focused_window, x, y, EnterNotify);
return;
}
x -= window_width;
}
- if(focused_window) {
- int x;
- int y;
- GetMonitorRelativeMousePosition(&x, &y);
- SendEnterLeaveNotifyEvent(display, focused_window, x, y, LeaveNotify);
- }
-
focused_window = None;
focused_window_offset_x = 0;
focused_window_offset_y = 0;
}
-bool CMainApplication::RemoveWindow(Display *display, Window window) {
+Window CMainApplication::GetFocusedWindowFromMousePosition(Window exclude_window) {
+ std::lock_guard<std::mutex> lock(pending_window_event_mutex);
+ int mx = mouse_x;
+ int my = mouse_y;
+
+ int x = 0;
+ const int window_width = m_nRenderWidth*2;
+ const int window_height = m_nRenderHeight;
+ for(auto &window_data : window_data_list) {
+ if(window_data->src_window_id == exclude_window)
+ continue;
+
+ if(mx >= x && mx <= x + window_width && my >= 0 && my <= window_height)
+ return window_data->src_window_id;
+
+ x -= window_width;
+ }
+
+ return None;
+}
+
+bool CMainApplication::RemoveWindow(Window window) {
for(auto it = window_data_list.begin(), end = window_data_list.end(); it != end; ++it) {
if((*it)->src_window_id == window) {
window_texture_deinit(&(*it)->window_texture);
window_data_list.erase(it);
+ num_windows--;
fprintf(stderr, "removed window: %ld\n", window);
fflush(stderr);
@@ -516,7 +491,7 @@ bool CMainApplication::RemoveWindow(Display *display, Window window) {
mouse_x = glm::clamp(mouse_x + 0, min_x, (int)(m_nRenderWidth*2));
mouse_y = glm::clamp(mouse_y + 0, 0, (int)m_nRenderHeight);
- SetFocusedWindowFromMousePosition(display);
+ SetFocusedWindowFromMousePosition();
return true;
}
}
@@ -539,111 +514,40 @@ void CMainApplication::GetFocusedWindowData(Window *window, int *x, int *y) {
GetMonitorRelativeMousePosition(x, y);
}
-void CMainApplication::MoveCursor(Display *display, int diff_x, int diff_y) {
+Window CMainApplication::MoveCursor(Display *display, int diff_x, int diff_y, CursorWrapSide cursor_wrap_side) {
if(holding_alt_key) {
window_rotation_offset += diff_x*2;
if(monitor_rotation_move_cursor) {
diff_x = -diff_x*2;
diff_y = 0;
} else {
- return;
+ std::lock_guard<std::mutex> lock(pending_window_event_mutex);
+ return focused_window;
}
}
- {
- std::lock_guard<std::mutex> lock(pending_window_event_mutex);
- int min_x = 0;
- if(!window_data_list.empty())
- min_x = -(int)((window_data_list.size() - 1) * (m_nRenderWidth*2));
- mouse_x = glm::clamp(mouse_x + diff_x, min_x, (int)(m_nRenderWidth*2));
- mouse_y = glm::clamp(mouse_y + diff_y, 0, (int)m_nRenderHeight);
- SetFocusedWindowFromMousePosition(display);
- }
-
- Window target_window;
- int x;
- int y;
- GetFocusedWindowData(&target_window, &x, &y);
-
- /* TODO: Should this be sent even without any focused window? */
- if(!target_window)
- return;
-
- XEvent fake_event;
- memset(&fake_event, 0, sizeof(fake_event));
- fake_event.type = MotionNotify;
- fake_event.xmotion.x = x;
- fake_event.xmotion.y = y;
- fake_event.xmotion.window = target_window;
- fake_event.xmotion.root = DefaultRootWindow(display);
- fake_event.xmotion.time = CurrentTime;
- if(XSendEvent(display, target_window, True, 0xfff, &fake_event) == 0)
- fprintf(stderr, "fake motion notify event failed!\n");
- XFlush(display);
-}
-
-void CMainApplication::KeyCallback(Display *display, int type, unsigned int state, unsigned int keycode) {
- assert(type == KeyPress || type == KeyRelease);
- Window target_window;
- int x;
- int y;
- GetFocusedWindowData(&target_window, &x, &y);
-
- /* TODO: Should this be sent even without any focused window? */
- if(!target_window)
- return;
-
- XEvent fake_event;
- memset(&fake_event, 0, sizeof(fake_event));
- fake_event.type = type;
- fake_event.xkey.type = type;
- fake_event.xkey.window = target_window;
- fake_event.xkey.root = DefaultRootWindow(display);
- fake_event.xkey.subwindow = target_window;
- fake_event.xkey.time = CurrentTime;
- fake_event.xkey.x = x;
- fake_event.xkey.y = y;
- fake_event.xkey.state = state;
- fake_event.xkey.keycode = keycode;
- if(XSendEvent(display, target_window, True, 0xfff, &fake_event) == 0)
- fprintf(stderr, "fake key event failed!\n");
-
- /* TODO: Remove flush? */
- XFlush(display);
+ std::lock_guard<std::mutex> lock(pending_window_event_mutex);
+ int min_x = 0;
+ if(!window_data_list.empty())
+ min_x = -(int)((window_data_list.size() - 1) * (m_nRenderWidth*2));
+ mouse_x = glm::clamp(mouse_x + diff_x, min_x, (int)(m_nRenderWidth*2));
+ mouse_y = glm::clamp(mouse_y + diff_y, 0, (int)m_nRenderHeight);
+ SetFocusedWindowFromMousePosition();
+ return focused_window;
}
void CMainApplication::ButtonCallback(Display *display, int type, unsigned int state, unsigned int button) {
assert(type == ButtonPress || type == ButtonRelease);
- Window target_window;
- int x;
- int y;
- GetFocusedWindowData(&target_window, &x, &y);
+
+ /* Left mouse button */
+ if(button != Button1)
+ return;
/* TODO: Check if other mod keys are not pressed (take numlock into consideration) */
if(state & Mod1Mask)
holding_alt_key = (type == ButtonPress);
-
- /* TODO: Should this be sent even without any focused window? */
- if(!target_window)
- return;
-
- XEvent fake_event;
- memset(&fake_event, 0, sizeof(fake_event));
- fake_event.type = type;
- fake_event.xbutton.type = type;
- fake_event.xbutton.window = target_window;
- fake_event.xbutton.root = DefaultRootWindow(display);
- fake_event.xbutton.subwindow = target_window;
- fake_event.xbutton.time = CurrentTime;
- fake_event.xbutton.x = x;
- fake_event.xbutton.y = y;
- fake_event.xbutton.state = state;
- fake_event.xbutton.button = button;
- if(XSendEvent(display, target_window, True, 0xfff, &fake_event) == 0)
- fprintf(stderr, "fake button event failed!\n");
-
- /* TODO: Remove flush? */
- XFlush(display);
+ else if(holding_alt_key && type == ButtonRelease)
+ holding_alt_key = false;
}
static void window_add_callback(Window window, void *userdata) {
@@ -653,11 +557,12 @@ static void window_add_callback(Window window, void *userdata) {
fflush(stderr);
}
-static void window_remove_callback(Window window, void *userdata) {
- CMainApplication *main_app = static_cast<CMainApplication*>(userdata);
- main_app->AddPendingWindowEvent(window, PendingWindowEventType::REMOVE);
+static Window window_remove_callback(Window window, void *userdata) {
fprintf(stderr, "pending window remove: %ld\n", window);
fflush(stderr);
+ CMainApplication *main_app = static_cast<CMainApplication*>(userdata);
+ main_app->AddPendingWindowEvent(window, PendingWindowEventType::REMOVE);
+ return main_app->GetFocusedWindowFromMousePosition(window);
}
static void reset_camera_callback(void *userdata) {
@@ -665,14 +570,9 @@ static void reset_camera_callback(void *userdata) {
main_app->m_bResetRotation = true;
}
-static void move_cursor_callback(Display *display, int diff_x, int diff_y, void *userdata) {
- CMainApplication *main_app = static_cast<CMainApplication*>(userdata);
- main_app->MoveCursor(display, diff_x, diff_y);
-}
-
-static void key_callback(Display *display, int type, unsigned int state, unsigned int keycode, void *userdata) {
+static Window move_cursor_callback(Display *display, int diff_x, int diff_y, CursorWrapSide cursor_wrap_side, void *userdata) {
CMainApplication *main_app = static_cast<CMainApplication*>(userdata);
- main_app->KeyCallback(display, type, state, keycode);
+ return main_app->MoveCursor(display, diff_x, diff_y, cursor_wrap_side);
}
static void button_callback(Display *display, int type, unsigned int state, unsigned int button, void *userdata) {
@@ -838,7 +738,6 @@ bool CMainApplication::BInit()
callback.remove_window_callback = window_remove_callback;
callback.reset_camera_callback = reset_camera_callback;
callback.move_cursor_callback = move_cursor_callback;
- callback.key_callback = key_callback;
callback.button_callback = button_callback;
if(window_manager_init(&window_manager, m_nRenderWidth*2, m_nRenderHeight, &callback, this) != 0) {
fprintf(stderr, "Failed to initialize the window manager!\n");
@@ -1098,7 +997,7 @@ bool CMainApplication::HandleInput()
AddWindow(window_event.window);
break;
case PendingWindowEventType::REMOVE:
- RemoveWindow(x_display, window_event.window);
+ RemoveWindow(window_event.window);
break;
}
}
@@ -2096,8 +1995,8 @@ CMainApplication *pMainApplication;
int main(int argc, char *argv[])
{
pMainApplication = new CMainApplication( argc, argv );
- /*freopen("/home/dec05eba/vrwm.stdout.log", "w", stdout);
- freopen("/home/dec05eba/vrwm.stderr.log", "w", stderr);*/
+ freopen("/home/dec05eba/vrwm.stdout.log", "w", stdout);
+ freopen("/home/dec05eba/vrwm.stderr.log", "w", stderr);
if (!pMainApplication->BInit())
{
diff --git a/src/window_manager.c b/src/window_manager.c
index 159348d..954bbf2 100644
--- a/src/window_manager.c
+++ b/src/window_manager.c
@@ -8,6 +8,11 @@
#include <X11/cursorfont.h>
*/
+/* TODO: Check xrandr version */
+#include <X11/extensions/Xrandr.h>
+#include <X11/extensions/XI2.h>
+#include <X11/extensions/XInput2.h>
+
#include <unistd.h>
#include <sys/wait.h>
@@ -21,6 +26,7 @@
static char *const menu_args[] = { "rofi", "-modi", "window,drun,combi", "-show", "combi", "-combi-modi", "window,drun", "-show-icons", NULL };
+/* TODO: Pipe stdout and stderr to /dev/null. Right now the output is seen in the window manager log */
static void spawn(Display *dpy, char *const args[]) {
if(fork() == 0) {
if (dpy)
@@ -33,34 +39,75 @@ static void spawn(Display *dpy, char *const args[]) {
}
}
-static int window_manager_contains_window(WindowManager *self, Window window) {
+/* Returns the id of the window or -1 */
+static int window_manager_get_window_index(WindowManager *self, Window window) {
for(int i = 0; i < WINDOW_MANAGER_MAX_WINDOWS; ++i) {
if(self->window_data[i].window == window)
- return 1;
+ return i;
}
- return 0;
+ return -1;
}
+/* Returns the id to the added window, or -1 */
static int window_manager_add_window(WindowManager *self, Window window) {
- if(window_manager_contains_window(self, window))
+ if(window_manager_get_window_index(self, window) != -1)
return 0;
for(int i = 0; i < WINDOW_MANAGER_MAX_WINDOWS; ++i) {
if(self->window_data[i].window == None) {
self->window_data[i].window = window;
- return 1;
+ self->window_data[i].visible = 0;
+ return i;
}
}
- return 0;
+ fprintf(stderr, "Window manager error: failed to add window: %ld. The window manager is already handling the max number of windows (%d)", window, WINDOW_MANAGER_MAX_WINDOWS);
+ return -1;
}
static int window_manager_remove_window(WindowManager *self, Window window) {
- for(int i = 0; i < WINDOW_MANAGER_MAX_WINDOWS; ++i) {
- if(self->window_data[i].window == window) {
- self->window_data[i].window = None;
- return 1;
+ int window_index = window_manager_get_window_index(self, window);
+ if(window_index != -1) {
+ self->window_data[window_index].window = None;
+ self->window_data[window_index].visible = 0;
+ return 1;
+ }
+ return 0;
+}
+
+static int window_manager_show_window(WindowManager *self, Window window, Display *display) {
+ int window_index = window_manager_get_window_index(self, window);
+ if(window_index != -1) {
+ self->window_data[window_index].visible = 1;
+ XMoveResizeWindow(display, window, 0, 0, self->window_width, self->window_height);
+ XRaiseWindow(display, window);
+ return 1;
+ }
+ return 0;
+}
+
+static int window_manager_hide_window(WindowManager *self, Window window, Display *display) {
+ int window_index = window_manager_get_window_index(self, window);
+ if(window_index != -1) {
+ self->window_data[window_index].visible = 0;
+ XMoveResizeWindow(display, window, -self->window_width - 100, 0, self->window_width, self->window_height);
+ XLowerWindow(display, window);
+ return 1;
+ }
+ return 0;
+}
+
+static int window_manager_update_visiblity(WindowManager *self, Window window, Display *display) {
+ int window_index = window_manager_get_window_index(self, window);
+ if(window_index != -1) {
+ if(self->window_data[window_index].visible) {
+ XMoveResizeWindow(display, window, 0, 0, self->window_width, self->window_height);
+ XRaiseWindow(display, window);
+ } else {
+ XMoveResizeWindow(display, window, -self->window_width - 100, 0, self->window_width, self->window_height);
+ XLowerWindow(display, window);
}
+ return 1;
}
return 0;
}
@@ -69,20 +116,43 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee) {
return 0;
}
+/* TODO: XRRSetCrtcTransform, XRRSetCrtcConfig (see ~/xrandr-test.c) */
+
static void* thread_main(void *userdata) {
WindowManager *window_manager = userdata;
Display * dpy;
Window root;
- XWindowAttributes attr;
- XButtonEvent start;
XEvent ev;
+ Window focused_window = None;
+
+ if(!(dpy = XOpenDisplay(NULL))) {
+ fprintf(stderr, "Failed to open x11 display!");
+ abort();
+ return NULL;
+ }
- if(!(dpy = XOpenDisplay(0x0))) return NULL;
+ int xiOpcode, queryEvent, queryError;
+ if (!XQueryExtension(dpy, "XInputExtension", &xiOpcode,
+ &queryEvent, &queryError)) {
+ fprintf(stderr, "X Input extension not available\n");
+ abort();
+ return NULL;
+ }
+
+ /* Check XInput 2.0 */
+ int major = 2;
+ int minor = 0;
+ int retval = XIQueryVersion(dpy, &major, &minor);
+ if (retval != Success) {
+ fprintf(stderr, "Error: XInput 2.0 is not supported (ancient X11?)\n");
+ abort();
+ return NULL;
+ }
root = DefaultRootWindow(dpy);
- XSelectInput(dpy, root, SubstructureRedirectMask | SubstructureNotifyMask);
+ XSelectInput(dpy, root, SubstructureRedirectMask | SubstructureNotifyMask /*| ResizeRedirectMask*/);
XSync(dpy, False);
KeyCode q_key = XKeysymToKeycode(dpy, XStringToKeysym("q"));
@@ -92,114 +162,206 @@ static void* thread_main(void *userdata) {
XGrabKey(dpy, q_key, Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, f1_key, Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, f12_key, Mod1Mask, root, True, GrabModeAsync, GrabModeAsync);
- XGrabButton(dpy, 1, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
- GrabModeAsync, None, None);
- XGrabButton(dpy, 3, Mod1Mask, root, True, ButtonPressMask, GrabModeAsync,
- GrabModeAsync, None, None);
-
- int s = DefaultScreen(dpy);
- int cursor_center_x = 64;
- int cursor_center_y = 64;
- Window cursor_window = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 128, 128, 1, BlackPixel(dpy, s), WhitePixel(dpy, s));
- XMapWindow(dpy, cursor_window);
+ XGrabButton(dpy, Button1, Mod1Mask, root, True, ButtonPressMask|ButtonReleaseMask,
+ GrabModeAsync,
+ GrabModeAsync, None, None);
+
+ int cursor_prev_x = 0;
+ int cursor_prev_y = 0;
+ Window dummy_w;
+ int dummy_int;
+ unsigned int dummy_m;
+ XWarpPointer(dpy, root, root, 0, 0, window_manager->window_width, window_manager->window_height, 0, 0);
XSync(dpy, False);
- XFlush(dpy);
+ /*XQueryPointer(dpy, root, &dummy_w, &dummy_w, &cursor_prev_x, &cursor_prev_y, &dummy_int, &dummy_int, &dummy_m);*/
- int grabbed_pointer = -1;
- int cursor_moved_once = 0;
- for(;;) {
- XNextEvent(dpy, &ev);
+ XIEventMask xi_masks;
+ unsigned char mask[(XI_LASTEVENT + 7)/8];
+ memset(mask, 0, sizeof(mask));
+ /* TODO: Use XI_Motion instead. It doesn't seem to work now for some reason. Or use XI_RawMotion without XQueryPointer and detect absolute position from relative position sum */
+ XISetMask(mask, XI_RawMotion);
- if(grabbed_pointer != GrabSuccess) {
- int event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; /*KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;*/
- grabbed_pointer = XGrabPointer(dpy, cursor_window, False,
- event_mask,
- GrabModeAsync, GrabModeAsync, cursor_window, None, CurrentTime);
- }
+ xi_masks.deviceid = XIAllMasterDevices;
+ xi_masks.mask_len = sizeof(mask);
+ xi_masks.mask = mask;
+
+ XISelectEvents(dpy, root, &xi_masks, 1);
- if(ev.type == MotionNotify && ev.xmotion.window == cursor_window) {
- while(XCheckTypedWindowEvent(dpy, cursor_window, MotionNotify, &ev));
+ int x11_fd = ConnectionNumber(dpy);
+ fd_set in_fds;
+ struct timeval tv;
- int diff_x = ev.xmotion.x - cursor_center_x;
- int diff_y = ev.xmotion.y - cursor_center_y;
- if(diff_x != 0 || diff_y != 0) {
- if(cursor_moved_once) {
- window_manager->callback.move_cursor_callback(dpy, diff_x, diff_y, window_manager->userdata);
+ while(window_manager->running) {
+ FD_ZERO(&in_fds);
+ FD_SET(x11_fd, &in_fds);
+
+ /* Wake up thread every 100 ms */
+ tv.tv_usec = 1000 * 100;
+ tv.tv_sec = 0;
+
+ int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv);
+ if (num_ready_fds > 0) {
+ /* Event received! */
+ } else if (num_ready_fds == 0) {
+ continue;
+ } else {
+ fprintf(stderr, "Window manager error: select failed on display file descriptor!\n");
+ continue;
+ }
+
+ while(window_manager->running && XPending(dpy)) {
+ XNextEvent(dpy, &ev);
+ XGenericEventCookie *cookie = (XGenericEventCookie*)&ev.xcookie;
+
+ Bool got_event_data = XGetEventData(dpy, cookie);
+ if(got_event_data && cookie->type == GenericEvent && cookie->extension == xiOpcode && cookie->evtype == XI_RawMotion) {
+ /* TODO: while(XCheckTypedWindowEvent(dpy, ev.xmotion.window, MotionNotify, &ev)); ? */
+ XIRawEvent *raw_ev = cookie->data;
+ //printf("move cursor, %f, %f\n", raw_ev->raw_values[0], raw_ev->raw_values[1]);
+
+ Window root_return, child_return;
+ int root_x_return, root_y_return;
+ int win_x_return, win_y_return;
+ unsigned int mask_return;
+ int retval = XQueryPointer(dpy, root, &root_return, &child_return,
+ &root_x_return, &root_y_return,
+ &win_x_return, &win_y_return,
+ &mask_return);
+ if(retval) {
+ int diff_x = root_x_return - cursor_prev_x;
+ int diff_y = root_y_return - cursor_prev_y;
+
+ /* TODO: Detect fast cursor movement and move it that distance from the edge?. It it needed? */
+ int new_cursor_x = root_x_return;
+ CursorWrapSide cursor_wrap_side = CURSOR_WRAP_NONE;
+ if(new_cursor_x <= 0) {
+ new_cursor_x = window_manager->window_width;
+ cursor_wrap_side = CURSOR_WRAP_LEFT;
+ } else if(new_cursor_x >= window_manager->window_width) {
+ new_cursor_x = 0;
+ cursor_wrap_side = CURSOR_WRAP_RIGHT;
+ }
+
+ fprintf(stderr, "move cursor!\n");
+ fflush(stderr);
+
+ cursor_prev_x = new_cursor_x;
+ cursor_prev_y = root_y_return;
+
+ if(cursor_wrap_side != CURSOR_WRAP_NONE) {
+ //XWarpPointer(dpy, root, root, 0, 0, window_manager->window_width, window_manager->window_height, new_cursor_x, root_y_return);
+ //XSync(dpy, False);
+ }
+
+ Window new_focused_window = window_manager->callback.move_cursor_callback(dpy, diff_x, diff_y, cursor_wrap_side, window_manager->userdata);
+ if(new_focused_window != focused_window) {
+ window_manager_hide_window(window_manager, focused_window, dpy);
+ window_manager_show_window(window_manager, new_focused_window, dpy);
+ focused_window = new_focused_window;
+ }
}
- cursor_moved_once = 1;
- XWarpPointer(dpy, None, cursor_window, 0, 0, 0, 0, cursor_center_x, cursor_center_y);
- XSync(dpy, False);
}
- } else if(ev.type == ButtonPress || ev.type == ButtonRelease) {
- window_manager->callback.button_callback(dpy, ev.xbutton.type, ev.xbutton.state, ev.xbutton.button, window_manager->userdata);
- }
+ if(got_event_data)
+ XFreeEventData(dpy, cookie);
- if(ev.type == KeyPress) {
- if(ev.xkey.keycode == f1_key) {
- window_manager->callback.reset_camera_callback(window_manager->userdata);
- } else if(ev.xkey.keycode == f12_key) {
- spawn(dpy, menu_args);
- } else if(ev.xkey.keycode == q_key && ev.xkey.subwindow && ev.xkey.subwindow != cursor_window) {
- XGrabServer(dpy);
- XErrorHandler prev_err_handler = XSetErrorHandler(xerrordummy);
- XSetCloseDownMode(dpy, DestroyAll);
- XKillClient(dpy, ev.xkey.subwindow);
- XSync(dpy, False);
- XSetErrorHandler(prev_err_handler);
- XUngrabServer(dpy);
- } else {
- /*window_manager->callback.key_callback(dpy, ev.xkey.type, ev.xkey.state, ev.xkey.keycode, window_manager->userdata);*/
+ if(ev.type == ButtonPress || ev.type == ButtonRelease) {
+ window_manager->callback.button_callback(dpy, ev.xbutton.type, ev.xbutton.state, ev.xbutton.button, window_manager->userdata);
}
- /*} else if(ev.type == ButtonPress && ev.xbutton.subwindow != None) {
- XGrabPointer(dpy, ev.xbutton.subwindow, True,
- PointerMotionMask|ButtonReleaseMask, GrabModeAsync,
- GrabModeAsync, None, None, CurrentTime);
- XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr);
- start = ev.xbutton;
- } else if(ev.type == MotionNotify) {
- int xdiff, ydiff;
- while(XCheckTypedEvent(dpy, MotionNotify, &ev));
- xdiff = ev.xbutton.x_root - start.x_root;
- ydiff = ev.xbutton.y_root - start.y_root;
- XMoveResizeWindow(dpy, ev.xmotion.window,
- attr.x + (start.button==1 ? xdiff : 0),
- attr.y + (start.button==1 ? ydiff : 0),
- MAX(1, attr.width + (start.button==3 ? xdiff : 0)),
- MAX(1, attr.height + (start.button==3 ? ydiff : 0)));
- } else if(ev.type == ButtonRelease) {
- XUngrabPointer(dpy, CurrentTime);
- */
- } else if(ev.type == KeyRelease) {
- /*window_manager->callback.key_callback(dpy, ev.xkey.type, ev.xkey.state, ev.xkey.keycode, window_manager->userdata);*/
- } else if(ev.type == MapRequest && ev.xmaprequest.window != cursor_window) {
- fprintf(stderr, "map request 1\n");
- fflush(stderr);
- Window window = ev.xmaprequest.window;
- XWindowAttributes wa;
- if (!XGetWindowAttributes(dpy, window, &wa))
- continue;
- if (wa.override_redirect)
- continue;
-
- XMoveResizeWindow(dpy, window, 129, 129, window_manager->window_width, window_manager->window_height);
- XMapWindow(dpy, window);
-
- if(window_manager_add_window(window_manager, window)) {
- fprintf(stderr, "added window\n");
+
+ if(ev.type == KeyPress) {
+ if(ev.xkey.keycode == f1_key) {
+ window_manager->callback.reset_camera_callback(window_manager->userdata);
+ } else if(ev.xkey.keycode == f12_key) {
+ spawn(dpy, menu_args);
+ } else if(ev.xkey.keycode == q_key && ev.xkey.subwindow) {
+ /* TODO: Check if this is needed. Wont DestroyNotify be called anyways where this is also done? */
+ /*
+ if(window_manager_remove_window(window_manager, ev.xkey.subwindow)) {
+ Window new_focused_window = window_manager->callback.remove_window_callback(ev.xkey.subwindow, window_manager->userdata);
+ if(new_focused_window != focused_window) {
+ window_manager_hide_window(window_manager, focused_window, dpy);
+ window_manager_show_window(window_manager, new_focused_window, dpy);
+ focused_window = new_focused_window;
+ }
+ }
+ */
+
+ XGrabServer(dpy);
+ XErrorHandler prev_err_handler = XSetErrorHandler(xerrordummy);
+ XSetCloseDownMode(dpy, DestroyAll);
+ XKillClient(dpy, ev.xkey.subwindow);
+ XSync(dpy, False);
+ XSetErrorHandler(prev_err_handler);
+ XUngrabServer(dpy);
+ } else {
+ /*window_manager->callback.key_callback(dpy, ev.xkey.type, ev.xkey.state, ev.xkey.keycode, window_manager->userdata);*/
+ }
+ /*} else if(ev.type == ButtonPress && ev.xbutton.subwindow != None) {
+ XGrabPointer(dpy, ev.xbutton.subwindow, True,
+ PointerMotionMask|ButtonReleaseMask, GrabModeAsync,
+ GrabModeAsync, None, None, CurrentTime);
+ XGetWindowAttributes(dpy, ev.xbutton.subwindow, &attr);
+ start = ev.xbutton;
+ } else if(ev.type == MotionNotify) {
+ int xdiff, ydiff;
+ while(XCheckTypedEvent(dpy, MotionNotify, &ev));
+ xdiff = ev.xbutton.x_root - start.x_root;
+ ydiff = ev.xbutton.y_root - start.y_root;
+ XMoveResizeWindow(dpy, ev.xmotion.window,
+ attr.x + (start.button==1 ? xdiff : 0),
+ attr.y + (start.button==1 ? ydiff : 0),
+ MAX(1, attr.width + (start.button==3 ? xdiff : 0)),
+ MAX(1, attr.height + (start.button==3 ? ydiff : 0)));
+ } else if(ev.type == ButtonRelease) {
+ XUngrabPointer(dpy, CurrentTime);
+ */
+ } else if(ev.type == KeyRelease) {
+ /*window_manager->callback.key_callback(dpy, ev.xkey.type, ev.xkey.state, ev.xkey.keycode, window_manager->userdata);*/
+ } else if(ev.type == MapRequest) {
+ fprintf(stderr, "map request 1\n");
fflush(stderr);
- window_manager->callback.add_window_callback(window, window_manager->userdata);
+ Window window = ev.xmaprequest.window;
+ XWindowAttributes wa;
+ if (!XGetWindowAttributes(dpy, window, &wa))
+ continue;
+ if (wa.override_redirect)
+ continue;
+
+ int window_index = window_manager_add_window(window_manager, window);
+ if(window_index != -1) {
+ if(!focused_window) {
+ focused_window = window;
+ window_manager->window_data[window_index].visible = 1;
+ XMoveResizeWindow(dpy, window, 0, 0, window_manager->window_width, window_manager->window_height);
+ XRaiseWindow(dpy, window);
+ } else {
+ XMoveResizeWindow(dpy, window, -window_manager->window_width - 100, 0, window_manager->window_width, window_manager->window_height);
+ XLowerWindow(dpy, window);
+ }
+ XMapWindow(dpy, window);
+ fprintf(stderr, "added window\n");
+ fflush(stderr);
+ window_manager->callback.add_window_callback(window, window_manager->userdata);
+ }
+ /* TODO: Do we need to map if window_index == -1 ? */
+ } else if(ev.type == DestroyNotify) {
+ Window window = ev.xdestroywindow.window;
+ if(window_manager_remove_window(window_manager, window)) {
+ Window new_focused_window = window_manager->callback.remove_window_callback(window, window_manager->userdata);
+ if(new_focused_window != focused_window) {
+ window_manager_hide_window(window_manager, focused_window, dpy);
+ window_manager_show_window(window_manager, new_focused_window, dpy);
+ focused_window = new_focused_window;
+ }
+ }
+ } else if(ev.type == ConfigureNotify) {
+ Window window = ev.xconfigure.window;
+ while(XCheckTypedWindowEvent(dpy, window, ConfigureNotify, &ev));
+ window_manager_update_visiblity(window_manager, window, dpy);
}
- } else if(ev.type == DestroyNotify && ev.xdestroywindow.window != cursor_window) {
- Window window = ev.xdestroywindow.window;
- if(window_manager_remove_window(window_manager, window))
- window_manager->callback.remove_window_callback(window, window_manager->userdata);
- }/* else if(ev.type == ConfigureNotify && ev.xconfigure.window != cursor_window) {
- Window window = ev.xconfigure.window;
- // Compress events
- while(XCheckTypedWindowEvent(dpy, window, ConfigureNotify, &ev));
- XMoveResizeWindow(dpy, window, 129, 129, window_manager->window_width, window_manager->window_height);
- }*/
+ /* TODO: Override unmap to not allow programs to hide windows (if the window is a child of the root window! sub windows such as popup windows should be allowed to hide, or maybe check window type?) */
+ }
}
XCloseDisplay(dpy);
@@ -212,9 +374,16 @@ int window_manager_init(WindowManager *self, int window_width, int window_height
assert(callback->remove_window_callback);
assert(callback->reset_camera_callback);
assert(callback->move_cursor_callback);
- assert(callback->key_callback);
assert(callback->button_callback);
+ /* TODO: Temporary! Replace with xrandr calls */
+ char command[2048];
+ snprintf(command, sizeof(command), "xrandr --output DP-0 --mode 1920x1080 --pos 0x0 --panning %dx%d+0+0 --rate 144 --scale-from %dx%d", window_width, window_height, window_width, window_height);
+ system(command);
+
+ snprintf(command, sizeof(command), "xrandr --output DP-4 --mode 3840x2160 --pos 0x0 --panning %dx%d+0+0 --rate 60 --scale-from %dx%d", window_width, window_height, window_width, window_height);
+ system(command);
+
memset(self->window_data, 0, sizeof(self->window_data));
self->callback = *callback;
self->window_width = window_width;
@@ -227,6 +396,6 @@ int window_manager_init(WindowManager *self, int window_width, int window_height
void window_manager_deinit(WindowManager *self) {
self->running = 0;
- /* TODO: Do this, but the window manager needs to be woken up somehow in its X11 event loop (which is isolated with its own Display connection) */
- /*pthread_join(self->thread, NULL);*/
+ /* May take up to 100ms (thread wakeup timeout) */
+ pthread_join(self->thread, NULL);
}