diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 2548dea2..673db319 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -82,6 +82,7 @@ video tutorials. - Jonathan Hale - hdf89shfdfs - Sylvain Hellegouarch + - Björn Hempel - Matthew Henry - heromyth - Lucas Hinderberger @@ -186,6 +187,7 @@ video tutorials. - Philip Rideout - Eddie Ringle - Max Risuhin + - Joe Roback - Jorge Rodriguez - Jari Ronkainen - Luca Rood diff --git a/README.md b/README.md index 64b34ddf..5b04649e 100644 --- a/README.md +++ b/README.md @@ -373,8 +373,10 @@ information on what to include when reporting a bug. wlroots compositors (#1268) - [Wayland] Bugfix: `GLFW_DECORATED` was ignored when showing a window with XDG decorations + - [Wayland] Bugfix: Connecting a mouse after `glfwInit` would segfault (#1450) - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled + - [Linux] Bugfix: Joysticks without buttons were ignored (#2042,#2043) - [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072) - [NSGL] Removed enforcement of forward-compatible flag for core contexts - [NSGL] Bugfix: `GLFW_COCOA_RETINA_FRAMEBUFFER` had no effect on newer diff --git a/src/linux_joystick.c b/src/linux_joystick.c index fd5dabdf..366bda2d 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -157,7 +157,7 @@ static GLFWbool openJoystickDevice(const char* path) } // Ensure this device supports the events expected of a joystick - if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits)) + if (!isBitSet(EV_ABS, evBits)) { close(linjs.fd); return GLFW_FALSE; diff --git a/src/wl_init.c b/src/wl_init.c index 5822cf39..97d4a2db 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -335,6 +335,38 @@ static void createKeyTables(void) } } +static GLFWbool loadCursorTheme(void) +{ + int cursorSize = 32; + + const char* sizeString = getenv("XCURSOR_SIZE"); + if (sizeString) + { + errno = 0; + const long cursorSizeLong = strtol(sizeString, NULL, 10); + if (errno == 0 && cursorSizeLong > 0 && cursorSizeLong < INT_MAX) + cursorSize = (int) cursorSizeLong; + } + + const char* themeName = getenv("XCURSOR_THEME"); + + _glfw.wl.cursorTheme = wl_cursor_theme_load(themeName, cursorSize, _glfw.wl.shm); + if (!_glfw.wl.cursorTheme) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to load default cursor theme"); + return GLFW_FALSE; + } + + // If this happens to be NULL, we just fallback to the scale=1 version. + _glfw.wl.cursorThemeHiDPI = + wl_cursor_theme_load(themeName, cursorSize * 2, _glfw.wl.shm); + + _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor); + _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + return GLFW_TRUE; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -472,14 +504,8 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) int _glfwInitWayland(void) { - const char* cursorTheme; - const char* cursorSizeStr; - char* cursorSizeEnd; - long cursorSizeLong; - int cursorSize; - // These must be set before any failure checks - _glfw.wl.timerfd = -1; + _glfw.wl.keyRepeatTimerfd = -1; _glfw.wl.cursorTimerfd = -1; _glfw.wl.client.display_flush = (PFN_wl_display_flush) @@ -640,7 +666,10 @@ int _glfwInitWayland(void) #ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION if (_glfw.wl.seatVersion >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) - _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + { + _glfw.wl.keyRepeatTimerfd = + timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + } #endif if (!_glfw.wl.wmBase) @@ -650,34 +679,16 @@ int _glfwInitWayland(void) return GLFW_FALSE; } - if (_glfw.wl.pointer && _glfw.wl.shm) + if (!_glfw.wl.shm) { - cursorTheme = getenv("XCURSOR_THEME"); - cursorSizeStr = getenv("XCURSOR_SIZE"); - cursorSize = 32; - if (cursorSizeStr) - { - errno = 0; - cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); - if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) - cursorSize = (int)cursorSizeLong; - } - _glfw.wl.cursorTheme = - wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); - if (!_glfw.wl.cursorTheme) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Failed to load default cursor theme"); - return GLFW_FALSE; - } - // If this happens to be NULL, we just fallback to the scale=1 version. - _glfw.wl.cursorThemeHiDPI = - wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); - _glfw.wl.cursorSurface = - wl_compositor_create_surface(_glfw.wl.compositor); - _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to find wl_shm in your compositor"); + return GLFW_FALSE; } + if (!loadCursorTheme()) + return GLFW_FALSE; + if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) { _glfw.wl.dataDevice = @@ -773,8 +784,8 @@ void _glfwTerminateWayland(void) wl_display_disconnect(_glfw.wl.display); } - if (_glfw.wl.timerfd >= 0) - close(_glfw.wl.timerfd); + if (_glfw.wl.keyRepeatTimerfd >= 0) + close(_glfw.wl.keyRepeatTimerfd); if (_glfw.wl.cursorTimerfd >= 0) close(_glfw.wl.cursorTimerfd); diff --git a/src/wl_platform.h b/src/wl_platform.h index 0da06d8d..2e88f2fb 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -322,12 +322,12 @@ typedef struct _GLFWlibraryWayland uint32_t serial; uint32_t pointerEnterSerial; - int32_t keyboardRepeatRate; - int32_t keyboardRepeatDelay; - int keyboardLastKey; - int keyboardLastScancode; + int keyRepeatTimerfd; + int32_t keyRepeatRate; + int32_t keyRepeatDelay; + int keyRepeatScancode; + char* clipboardString; - int timerfd; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; char keynames[GLFW_KEY_LAST + 1][5]; @@ -516,7 +516,6 @@ void _glfwSetGammaRampWayland(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwAddOutputWayland(uint32_t name, uint32_t version); void _glfwUpdateContentScaleWayland(_GLFWwindow* window); -GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode); void _glfwAddSeatListenerWayland(struct wl_seat* seat); void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); diff --git a/src/wl_window.c b/src/wl_window.c index 04415435..2797fd49 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -142,11 +142,8 @@ static int createAnonymousFile(off_t size) static struct wl_buffer* createShmBuffer(const GLFWimage* image) { - struct wl_shm_pool* pool; - struct wl_buffer* buffer; - int stride = image->width * 4; - int length = image->width * image->height * 4; - void* data; + const int stride = image->width * 4; + const int length = image->width * image->height * 4; const int fd = createAnonymousFile(length); if (fd < 0) @@ -157,7 +154,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) return NULL; } - data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + void* data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -166,9 +163,10 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) return NULL; } - pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); + struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); close(fd); + unsigned char* source = (unsigned char*) image->pixels; unsigned char* target = data; for (int i = 0; i < image->width * image->height; i++, source += 4) @@ -181,7 +179,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) *target++ = (unsigned char) alpha; } - buffer = + struct wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, image->width, image->height, @@ -743,7 +741,7 @@ static GLFWbool createNativeSurface(_GLFWwindow* window, static void setCursorImage(_GLFWwindow* window, _GLFWcursorWayland* cursorWayland) { - struct itimerspec timer = {}; + struct itimerspec timer = {0}; struct wl_cursor* wlCursor = cursorWayland->cursor; struct wl_cursor_image* image; struct wl_buffer* buffer; @@ -821,13 +819,59 @@ static GLFWbool flushDisplay(void) return GLFW_TRUE; } +static int translateKey(uint32_t scancode) +{ + if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) + return _glfw.wl.keycodes[scancode]; + + return GLFW_KEY_UNKNOWN; +} + +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) + { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} + +static void inputText(_GLFWwindow* window, uint32_t scancode) +{ + const xkb_keysym_t* keysyms; + const xkb_keycode_t keycode = scancode + 8; + + if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1) + { + const xkb_keysym_t keysym = composeSymbol(keysyms[0]); + const uint32_t codepoint = _glfwKeySym2Unicode(keysym); + if (codepoint != GLFW_INVALID_CODEPOINT) + { + const int mods = _glfw.wl.xkb.modifiers; + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, codepoint, mods, plain); + } + } +} + static void handleEvents(double* timeout) { GLFWbool event = GLFW_FALSE; struct pollfd fds[] = { { wl_display_get_fd(_glfw.wl.display), POLLIN }, - { _glfw.wl.timerfd, POLLIN }, + { _glfw.wl.keyRepeatTimerfd, POLLIN }, { _glfw.wl.cursorTimerfd, POLLIN }, }; @@ -871,17 +915,16 @@ static void handleEvents(double* timeout) { uint64_t repeats; - if (read(_glfw.wl.timerfd, &repeats, sizeof(repeats)) == 8) + if (read(_glfw.wl.keyRepeatTimerfd, &repeats, sizeof(repeats)) == 8) { for (uint64_t i = 0; i < repeats; i++) { _glfwInputKey(_glfw.wl.keyboardFocus, - _glfw.wl.keyboardLastKey, - _glfw.wl.keyboardLastScancode, + translateKey(_glfw.wl.keyRepeatScancode), + _glfw.wl.keyRepeatScancode, GLFW_PRESS, _glfw.wl.xkb.modifiers); - _glfwInputTextWayland(_glfw.wl.keyboardFocus, - _glfw.wl.keyboardLastScancode); + inputText(_glfw.wl.keyboardFocus, _glfw.wl.keyRepeatScancode); } event = GLFW_TRUE; @@ -1044,8 +1087,8 @@ static void pointerHandleLeave(void* userData, _glfw.wl.serial = serial; _glfw.wl.pointerFocus = NULL; - _glfwInputCursorEnter(window, GLFW_FALSE); _glfw.wl.cursorPreviousName = NULL; + _glfwInputCursorEnter(window, GLFW_FALSE); } static void setCursor(_GLFWwindow* window, const char* name) @@ -1116,8 +1159,8 @@ static void pointerHandleMotion(void* userData, switch (window->wl.decorations.focus) { case mainWindow: - _glfwInputCursorPos(window, x, y); _glfw.wl.cursorPreviousName = NULL; + _glfwInputCursorPos(window, x, y); return; case topDecoration: if (y < GLFW_BORDER_SIZE) @@ -1397,62 +1440,14 @@ static void keyboardHandleLeave(void* userData, if (!window) return; - struct itimerspec timer = {}; - timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); + struct itimerspec timer = {0}; + timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = NULL; _glfwInputWindowFocus(window, GLFW_FALSE); } -static int translateKey(uint32_t scancode) -{ - if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) - return _glfw.wl.keycodes[scancode]; - - return GLFW_KEY_UNKNOWN; -} - -static xkb_keysym_t composeSymbol(xkb_keysym_t sym) -{ - if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) - return sym; - if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) - != XKB_COMPOSE_FEED_ACCEPTED) - return sym; - switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) - { - case XKB_COMPOSE_COMPOSED: - return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); - case XKB_COMPOSE_COMPOSING: - case XKB_COMPOSE_CANCELLED: - return XKB_KEY_NoSymbol; - case XKB_COMPOSE_NOTHING: - default: - return sym; - } -} - -GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode) -{ - const xkb_keysym_t* keysyms; - const xkb_keycode_t keycode = scancode + 8; - - if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1) - { - const xkb_keysym_t keysym = composeSymbol(keysyms[0]); - const uint32_t codepoint = _glfwKeySym2Unicode(keysym); - if (codepoint != GLFW_INVALID_CODEPOINT) - { - const int mods = _glfw.wl.xkb.modifiers; - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - _glfwInputChar(window, codepoint, mods, plain); - } - } - - return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode); -} - static void keyboardHandleKey(void* userData, struct wl_keyboard* keyboard, uint32_t serial, @@ -1469,29 +1464,33 @@ static void keyboardHandleKey(void* userData, state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; _glfw.wl.serial = serial; - _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); - struct itimerspec timer = {}; + struct itimerspec timer = {0}; if (action == GLFW_PRESS) { - const GLFWbool shouldRepeat = _glfwInputTextWayland(window, scancode); + const xkb_keycode_t keycode = scancode + 8; - if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) + if (xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode) && + _glfw.wl.keyRepeatRate > 0) { - _glfw.wl.keyboardLastKey = key; - _glfw.wl.keyboardLastScancode = scancode; - if (_glfw.wl.keyboardRepeatRate > 1) - timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; + _glfw.wl.keyRepeatScancode = scancode; + if (_glfw.wl.keyRepeatRate > 1) + timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyRepeatRate; else timer.it_interval.tv_sec = 1; - timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; - timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; + timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000; + timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000; } } - timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); + timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); + + _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); + + if (action == GLFW_PRESS) + inputText(window, scancode); } static void keyboardHandleModifiers(void* userData, @@ -1551,8 +1550,8 @@ static void keyboardHandleRepeatInfo(void* userData, if (keyboard != _glfw.wl.keyboard) return; - _glfw.wl.keyboardRepeatRate = rate; - _glfw.wl.keyboardRepeatDelay = delay; + _glfw.wl.keyRepeatRate = rate; + _glfw.wl.keyRepeatDelay = delay; } #endif @@ -1843,15 +1842,10 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window, void _glfwDestroyWindowWayland(_GLFWwindow* window) { if (window == _glfw.wl.pointerFocus) - { _glfw.wl.pointerFocus = NULL; - _glfwInputCursorEnter(window, GLFW_FALSE); - } + if (window == _glfw.wl.keyboardFocus) - { _glfw.wl.keyboardFocus = NULL; - _glfwInputWindowFocus(window, GLFW_FALSE); - } if (window->wl.idleInhibitor) zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); diff --git a/tests/events.c b/tests/events.c index a9d4023d..60d4fc89 100644 --- a/tests/events.c +++ b/tests/events.c @@ -642,7 +642,7 @@ int main(int argc, char** argv) glfwMakeContextCurrent(slots[i].window); gladLoadGL(glfwGetProcAddress); - glfwSwapInterval(1); + glfwSwapBuffers(slots[i].window); } printf("Main loop starting\n");