diff options
Diffstat (limited to 'intern/ghost/intern')
-rw-r--r-- | intern/ghost/intern/GHOST_EventKey.h | 17 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemCocoa.mm | 23 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemSDL.cpp | 274 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWayland.cpp | 383 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWayland.h | 6 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemWin32.cpp | 5 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_SystemX11.cpp | 114 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_WindowManager.cpp | 14 | ||||
-rw-r--r-- | intern/ghost/intern/GHOST_WindowManager.h | 6 |
9 files changed, 483 insertions, 359 deletions
diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index 1c3156c27d2..f85a674be9f 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -22,13 +22,13 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. + * \param is_repeat: Enabled for key repeat events (only for press events). */ GHOST_EventKey( uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, bool is_repeat) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = '\0'; m_keyEventData.utf8_buf[0] = '\0'; m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; @@ -39,23 +39,24 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. - * \param ascii: The ascii code for the key event. + * \param is_repeat: Enabled for key repeat events (only for press events). + * \param utf8_buf: The text associated with this key event (only for press events). */ GHOST_EventKey(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, - char ascii, - const char utf8_buf[6], - bool is_repeat) + bool is_repeat, + const char utf8_buf[6]) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = ascii; - if (utf8_buf) + if (utf8_buf) { memcpy(m_keyEventData.utf8_buf, utf8_buf, sizeof(m_keyEventData.utf8_buf)); - else + } + else { m_keyEventData.utf8_buf[0] = '\0'; + } m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; } diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 0a905f5093e..c247ef3daa0 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1779,7 +1779,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) NSString *characters; NSData *convertedCharacters; GHOST_TKey keyCode; - unsigned char ascii; NSString *charsIgnoringModifiers; window = m_windowManager->getWindowAssociatedWithOSWindow((void *)[event window]); @@ -1789,7 +1788,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) } char utf8_buf[6] = {'\0'}; - ascii = 0; switch ([event type]) { @@ -1809,7 +1807,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) kUCKeyActionUp); } - /* handling both unicode or ascii */ characters = [event characters]; if ([characters length] > 0) { convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding]; @@ -1835,41 +1832,31 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSEventModifierFlagCommand)) break; // Cmd-Q is directly handled by Cocoa - /* ascii is a subset of unicode */ - if (utf8_buf[0] && !utf8_buf[1]) { - ascii = utf8_buf[0]; - } - if ([event type] == NSEventTypeKeyDown) { pushEvent(new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, - ascii, - utf8_buf, - [event isARepeat])); + [event isARepeat], + utf8_buf)); #if 0 - printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } else { pushEvent(new GHOST_EventKey( - [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL, false)); + [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, false, NULL)); #if 0 - printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index a29bfd9489d..d912b57f049 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -279,6 +279,141 @@ static GHOST_TKey convertSDLKey(SDL_Scancode key) } #undef GXMAP +static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt) +{ + SDL_Keycode sym = sdl_sub_evt.keysym.sym; + if (sym > 127) { + switch (sym) { + case SDLK_KP_DIVIDE: + sym = '/'; + break; + case SDLK_KP_MULTIPLY: + sym = '*'; + break; + case SDLK_KP_MINUS: + sym = '-'; + break; + case SDLK_KP_PLUS: + sym = '+'; + break; + case SDLK_KP_1: + sym = '1'; + break; + case SDLK_KP_2: + sym = '2'; + break; + case SDLK_KP_3: + sym = '3'; + break; + case SDLK_KP_4: + sym = '4'; + break; + case SDLK_KP_5: + sym = '5'; + break; + case SDLK_KP_6: + sym = '6'; + break; + case SDLK_KP_7: + sym = '7'; + break; + case SDLK_KP_8: + sym = '8'; + break; + case SDLK_KP_9: + sym = '9'; + break; + case SDLK_KP_0: + sym = '0'; + break; + case SDLK_KP_PERIOD: + sym = '.'; + break; + default: + sym = 0; + break; + } + } + else { + if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { + /* Weak US keyboard assumptions. */ + if (sym >= 'a' && sym <= ('a' + 32)) { + sym -= 32; + } + else { + switch (sym) { + case '`': + sym = '~'; + break; + case '1': + sym = '!'; + break; + case '2': + sym = '@'; + break; + case '3': + sym = '#'; + break; + case '4': + sym = '$'; + break; + case '5': + sym = '%'; + break; + case '6': + sym = '^'; + break; + case '7': + sym = '&'; + break; + case '8': + sym = '*'; + break; + case '9': + sym = '('; + break; + case '0': + sym = ')'; + break; + case '-': + sym = '_'; + break; + case '=': + sym = '+'; + break; + case '[': + sym = '{'; + break; + case ']': + sym = '}'; + break; + case '\\': + sym = '|'; + break; + case ';': + sym = ':'; + break; + case '\'': + sym = '"'; + break; + case ',': + sym = '<'; + break; + case '.': + sym = '>'; + break; + case '/': + sym = '?'; + break; + default: + break; + } + } + } + } + return (char)sym; +} + /** * Events don't always have valid windows, * but GHOST needs a window _always_. fallback to the GL window. @@ -454,9 +589,9 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) case SDL_KEYDOWN: case SDL_KEYUP: { SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key; - SDL_Keycode sym = sdl_sub_evt.keysym.sym; GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; + const bool is_repeat = sdl_sub_evt.repeat != 0; GHOST_WindowSDL *window = findGhostWindow( SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID)); @@ -465,138 +600,15 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode); /* NOTE: the `sdl_sub_evt.keysym.sym` is truncated, * for unicode support ghost has to be modified. */ - // printf("%d\n", sym); - if (sym > 127) { - switch (sym) { - case SDLK_KP_DIVIDE: - sym = '/'; - break; - case SDLK_KP_MULTIPLY: - sym = '*'; - break; - case SDLK_KP_MINUS: - sym = '-'; - break; - case SDLK_KP_PLUS: - sym = '+'; - break; - case SDLK_KP_1: - sym = '1'; - break; - case SDLK_KP_2: - sym = '2'; - break; - case SDLK_KP_3: - sym = '3'; - break; - case SDLK_KP_4: - sym = '4'; - break; - case SDLK_KP_5: - sym = '5'; - break; - case SDLK_KP_6: - sym = '6'; - break; - case SDLK_KP_7: - sym = '7'; - break; - case SDLK_KP_8: - sym = '8'; - break; - case SDLK_KP_9: - sym = '9'; - break; - case SDLK_KP_0: - sym = '0'; - break; - case SDLK_KP_PERIOD: - sym = '.'; - break; - default: - sym = 0; - break; - } - } - else { - if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { - /* lame US keyboard assumptions */ - if (sym >= 'a' && sym <= ('a' + 32)) { - sym -= 32; - } - else { - switch (sym) { - case '`': - sym = '~'; - break; - case '1': - sym = '!'; - break; - case '2': - sym = '@'; - break; - case '3': - sym = '#'; - break; - case '4': - sym = '$'; - break; - case '5': - sym = '%'; - break; - case '6': - sym = '^'; - break; - case '7': - sym = '&'; - break; - case '8': - sym = '*'; - break; - case '9': - sym = '('; - break; - case '0': - sym = ')'; - break; - case '-': - sym = '_'; - break; - case '=': - sym = '+'; - break; - case '[': - sym = '{'; - break; - case ']': - sym = '}'; - break; - case '\\': - sym = '|'; - break; - case ';': - sym = ':'; - break; - case '\'': - sym = '"'; - break; - case ',': - sym = '<'; - break; - case '.': - sym = '>'; - break; - case '/': - sym = '?'; - break; - default: - break; - } - } - } + + /* TODO(@campbellbarton): support full unicode, SDL supports this but it needs to be + * explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */ + char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; + if (type == GHOST_kEventKeyDown) { + utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt); } - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, nullptr, false); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); break; } } diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 08aa640c5cd..d7520f1243f 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -128,11 +128,6 @@ static bool use_gnome_confine_hack = false; */ #define EVDEV_OFFSET 8 -struct buffer_t { - void *data = nullptr; - size_t size = 0; -}; - struct cursor_t { bool visible = false; /** @@ -147,13 +142,11 @@ struct cursor_t { struct wl_buffer *wl_buffer = nullptr; struct wl_cursor_image wl_image = {0}; struct wl_cursor_theme *wl_theme = nullptr; - struct buffer_t *file_buffer = nullptr; + void *custom_data = nullptr; + size_t custom_data_size = 0; int size = 0; std::string theme_name; - /** Outputs on which the cursor is visible. */ - std::unordered_set<const output_t *> outputs; - int theme_scale = 1; int custom_scale = 1; }; @@ -235,6 +228,11 @@ struct input_state_pointer_t { */ wl_fixed_t xy[2] = {0, 0}; + /** Outputs on which the cursor is visible. */ + std::unordered_set<const output_t *> outputs; + + int theme_scale = 1; + /** The serial of the last used pointer or tablet. */ uint32_t serial = 0; @@ -400,6 +398,19 @@ static input_state_pointer_t *input_state_pointer_active(input_t *input) return nullptr; } +static input_state_pointer_t *input_state_pointer_from_cursor_surface(input_t *input, + const wl_surface *wl_surface) +{ + if (ghost_wl_surface_own_cursor_pointer(wl_surface)) { + return &input->pointer; + } + if (ghost_wl_surface_own_cursor_tablet(wl_surface)) { + return &input->tablet; + } + GHOST_ASSERT(0, "Surface found without pointer/tablet tag"); + return nullptr; +} + static void display_destroy(display_t *d) { if (d->data_device_manager) { @@ -449,11 +460,12 @@ static void display_destroy(display_t *d) if (input->data_device) { wl_data_device_release(input->data_device); } + + if (input->cursor.custom_data) { + munmap(input->cursor.custom_data, input->cursor.custom_data_size); + } + if (input->wl_pointer) { - if (input->cursor.file_buffer) { - munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); - delete input->cursor.file_buffer; - } if (input->cursor.wl_surface) { wl_surface_destroy(input->cursor.wl_surface); } @@ -747,7 +759,83 @@ static const std::vector<std::string> mime_send = { "text/plain", }; -#undef LOG +static int memfd_create_sealed(const char *name) +{ +#ifdef HAVE_MEMFD_CREATE + const int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) { + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } + return fd; +#else /* HAVE_MEMFD_CREATE */ + char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + char *tmpname; + asprintf(&tmpname, "%s/%s-XXXXXX", path, name); + const int fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) { + unlink(tmpname); + } + free(tmpname); + return fd; +#endif /* !HAVE_MEMFD_CREATE */ +} + +static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format) +{ + switch (format) { + case WL_SHM_FORMAT_ARGB8888: { + return 4; + } + default: { + /* Support other formats as needed. */ + GHOST_ASSERT(0, "Unexpected format passed in!"); + return 4; + } + } +} + +/** + * Return a #wl_buffer, ready to have it's data filled in or NULL in case of failure. + * The caller is responsible for calling `unmap(buffer_data, buffer_size)`. + * + * \param r_buffer_data: The buffer to be filled. + * \param r_buffer_data_size: The size of `r_buffer_data` in bytes. + */ +static wl_buffer *ghost_wl_buffer_create_for_image(struct wl_shm *shm, + const int32_t size_xy[2], + enum wl_shm_format format, + void **r_buffer_data, + size_t *r_buffer_data_size) +{ + const int fd = memfd_create_sealed("ghost-wl-buffer"); + wl_buffer *buffer = nullptr; + if (fd >= 0) { + const int32_t buffer_stride = size_xy[0] * ghost_wl_shm_format_as_size(format); + const int32_t buffer_size = buffer_stride * size_xy[1]; + if (posix_fallocate(fd, 0, buffer_size) == 0) { + void *buffer_data = mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer_data != MAP_FAILED) { + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, buffer_size); + buffer = wl_shm_pool_create_buffer(pool, 0, UNPACK2(size_xy), buffer_stride, format); + wl_shm_pool_destroy(pool); + if (buffer) { + *r_buffer_data = buffer_data; + *r_buffer_data_size = (size_t)buffer_size; + } + else { + /* Highly unlikely. */ + munmap(buffer_data, buffer_size); + } + } + } + close(fd); + } + return buffer; +} /** \} */ @@ -1285,19 +1373,22 @@ static const struct wl_buffer_listener cursor_buffer_listener = { static CLG_LogRef LOG_WL_CURSOR_SURFACE = {"ghost.wl.handle.cursor_surface"}; #define LOG (&LOG_WL_CURSOR_SURFACE) -static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) +static bool update_cursor_scale(cursor_t &cursor, + wl_shm *shm, + input_state_pointer_t *input_state, + wl_surface *cursor_surface) { int scale = 0; - for (const output_t *output : cursor.outputs) { + for (const output_t *output : input_state->outputs) { if (output->scale > scale) { scale = output->scale; } } - if (scale > 0 && cursor.theme_scale != scale) { - cursor.theme_scale = scale; + if (scale > 0 && input_state->theme_scale != scale) { + input_state->theme_scale = scale; if (!cursor.is_custom) { - wl_surface_set_buffer_scale(cursor.wl_surface, scale); + wl_surface_set_buffer_scale(cursor_surface, scale); } wl_cursor_theme_destroy(cursor.wl_theme); cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); @@ -1307,7 +1398,7 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) } static void cursor_surface_handle_enter(void *data, - struct wl_surface * /*wl_surface*/, + struct wl_surface *wl_surface, struct wl_output *output) { if (!ghost_wl_output_own(output)) { @@ -1317,13 +1408,14 @@ static void cursor_surface_handle_enter(void *data, CLOG_INFO(LOG, 2, "handle_enter"); input_t *input = static_cast<input_t *>(data); + input_state_pointer_t *input_state = input_state_pointer_from_cursor_surface(input, wl_surface); const output_t *reg_output = ghost_wl_output_user_data(output); - input->cursor.outputs.insert(reg_output); - update_cursor_scale(input->cursor, input->system->shm()); + input_state->outputs.insert(reg_output); + update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface); } static void cursor_surface_handle_leave(void *data, - struct wl_surface * /*wl_surface*/, + struct wl_surface *wl_surface, struct wl_output *output) { if (!(output && ghost_wl_output_own(output))) { @@ -1333,9 +1425,10 @@ static void cursor_surface_handle_leave(void *data, CLOG_INFO(LOG, 2, "handle_leave"); input_t *input = static_cast<input_t *>(data); + input_state_pointer_t *input_state = input_state_pointer_from_cursor_surface(input, wl_surface); const output_t *reg_output = ghost_wl_output_user_data(output); - input->cursor.outputs.erase(reg_output); - update_cursor_scale(input->cursor, input->system->shm()); + input_state->outputs.erase(reg_output); + update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface); } static const struct wl_surface_listener cursor_surface_listener = { @@ -1879,6 +1972,8 @@ static void tablet_seat_handle_tool_added(void *data, /* Every tool has it's own cursor surface. */ tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor()); + ghost_wl_surface_tag_cursor_tablet(tool_input->cursor_surface); + wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input); zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input); @@ -2162,8 +2257,8 @@ static void keyboard_handle_key(void *data, if (wl_surface *focus_surface = input->keyboard.wl_surface) { GHOST_IWindow *win = ghost_wl_surface_user_data(focus_surface); - input->system->pushEvent(new GHOST_EventKey( - input->system->getMilliSeconds(), etype, win, gkey, '\0', utf8_buf, false)); + input->system->pushEvent( + new GHOST_EventKey(input->system->getMilliSeconds(), etype, win, gkey, false, utf8_buf)); } /* An existing payload means the key repeat timer is reset and will be added again. */ @@ -2195,9 +2290,8 @@ static void keyboard_handle_key(void *data, GHOST_kEventKeyDown, win, payload->key_data.gkey, - '\0', - utf8_buf, - true)); + true, + utf8_buf)); } }; input->key_repeat.timer = input->system->installTimer( @@ -2282,13 +2376,14 @@ static void seat_handle_capabilities(void *data, input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor()); input->cursor.visible = true; input->cursor.wl_buffer = nullptr; - input->cursor.file_buffer = new buffer_t; if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { input->cursor.theme_name = std::string(); input->cursor.size = default_cursor_size; } wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data); + wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data); + ghost_wl_surface_tag_cursor_pointer(input->cursor.wl_surface); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { @@ -2502,7 +2597,17 @@ static void output_handle_done(void *data, struct wl_output * /*wl_output*/) static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, const int32_t factor) { + CLOG_INFO(LOG, 2, "scale"); static_cast<output_t *>(data)->scale = factor; + + if (window_manager) { + for (GHOST_IWindow *iwin : window_manager->getWindows()) { + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin); + win->outputs_changed_update_scale(); + /* TODO(@campbellbarton): support refreshing the UI when the DPI changes. + * There are glitches when resizing the monitor which would be nice to solve. */ + } + } } static const struct wl_output_listener output_listener = { @@ -3139,19 +3244,30 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, static void cursor_buffer_show(const input_t *input) { const cursor_t *c = &input->cursor; - const int scale = c->is_custom ? c->custom_scale : c->theme_scale; - const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; - const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; - wl_pointer_set_cursor( - input->wl_pointer, input->pointer.serial, c->wl_surface, hotspot_x, hotspot_y); - for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( - zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); - zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, - input->tablet.serial, - tool_input->cursor_surface, - hotspot_x, - hotspot_y); + + if (input->wl_pointer) { + const int scale = c->is_custom ? c->custom_scale : input->pointer.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + if (input->wl_pointer) { + wl_pointer_set_cursor( + input->wl_pointer, input->pointer.serial, c->wl_surface, hotspot_x, hotspot_y); + } + } + + if (!input->tablet_tools.empty()) { + const int scale = c->is_custom ? c->custom_scale : input->tablet.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + input->tablet.serial, + tool_input->cursor_surface, + hotspot_x, + hotspot_y); + } } } @@ -3169,52 +3285,77 @@ static void cursor_buffer_hide(const input_t *input) } } -static void cursor_buffer_set(const input_t *input, wl_buffer *buffer) +/** + * Needed to ensure the cursor size is always a multiple of scale. + */ +static int cursor_buffer_compatible_scale_from_image(const struct wl_cursor_image *wl_image, + int scale) { - const cursor_t *c = &input->cursor; - const int scale = c->is_custom ? c->custom_scale : c->theme_scale; - - const bool visible = (c->visible && c->is_hardware); - - const int32_t image_size_x = int32_t(c->wl_image.width); - const int32_t image_size_y = int32_t(c->wl_image.height); + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); + while (scale > 1) { + if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) { + break; + } + scale -= 1; + } + return scale; +} - /* This is a requirement of WAYLAND, when this isn't the case, - * it causes Blender's window to close intermittently. */ +static void cursor_buffer_set_surface_impl(const input_t *input, + wl_buffer *buffer, + struct wl_surface *wl_surface, + const int scale) +{ + const wl_cursor_image *wl_image = &input->cursor.wl_image; + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0, "The size must be a multiple of the scale!"); - const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; - const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + wl_surface_set_buffer_scale(wl_surface, scale); + wl_surface_attach(wl_surface, buffer, 0, 0); + wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y); + wl_surface_commit(wl_surface); +} - wl_surface_set_buffer_scale(c->wl_surface, scale); - wl_surface_attach(c->wl_surface, buffer, 0, 0); - wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(c->wl_surface); +static void cursor_buffer_set(const input_t *input, wl_buffer *buffer) +{ + const cursor_t *c = &input->cursor; + const wl_cursor_image *wl_image = &input->cursor.wl_image; + const bool visible = (c->visible && c->is_hardware); - wl_pointer_set_cursor(input->wl_pointer, - input->pointer.serial, - visible ? c->wl_surface : nullptr, - hotspot_x, - hotspot_y); + /* This is a requirement of WAYLAND, when this isn't the case, + * it causes Blender's window to close intermittently. */ + if (input->wl_pointer) { + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : input->pointer.theme_scale); + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + cursor_buffer_set_surface_impl(input, buffer, c->wl_surface, scale); + wl_pointer_set_cursor(input->wl_pointer, + input->pointer.serial, + visible ? c->wl_surface : nullptr, + hotspot_x, + hotspot_y); + } /* Set the cursor for all tablet tools as well. */ - for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( - zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); - - /* FIXME: for some reason cursor scale is applied twice (when the scale isn't 1x), - * this happens both in gnome-shell & KDE. Setting the surface scale here doesn't help. */ - wl_surface_set_buffer_scale(tool_input->cursor_surface, scale); - wl_surface_attach(tool_input->cursor_surface, buffer, 0, 0); - wl_surface_damage(tool_input->cursor_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(tool_input->cursor_surface); - - zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, - input->tablet.serial, - visible ? tool_input->cursor_surface : nullptr, - hotspot_x, - hotspot_y); + if (!input->tablet_tools.empty()) { + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : input->tablet.theme_scale); + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + cursor_buffer_set_surface_impl(input, buffer, tool_input->cursor_surface, scale); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + input->tablet.serial, + visible ? tool_input->cursor_surface : nullptr, + hotspot_x, + hotspot_y); + } } } @@ -3347,55 +3488,19 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, cursor_t *cursor = &d->inputs[0]->cursor; - static const int32_t stride = sizex * 4; /* ARGB */ - cursor->file_buffer->size = (size_t)stride * sizey; - -#ifdef HAVE_MEMFD_CREATE - const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) { - fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); - } -#else - char *path = getenv("XDG_RUNTIME_DIR"); - if (!path) { - errno = ENOENT; - return GHOST_kFailure; - } - - char *tmpname; - asprintf(&tmpname, "%s/%s", path, "blender-XXXXXX"); - const int fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) { - unlink(tmpname); + if (cursor->custom_data) { + munmap(cursor->custom_data, cursor->custom_data_size); + cursor->custom_data = nullptr; + cursor->custom_data_size = 0; /* Not needed, but the value is no longer meaningful. */ } - free(tmpname); -#endif - - if (UNLIKELY(fd < 0)) { - return GHOST_kFailure; - } - - if (UNLIKELY(posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)) != 0)) { - return GHOST_kFailure; - } - - cursor->file_buffer->data = mmap( - nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (UNLIKELY(cursor->file_buffer->data == MAP_FAILED)) { - cursor->file_buffer->data = nullptr; - close(fd); + const int32_t size_xy[2] = {sizex, sizey}; + wl_buffer *buffer = ghost_wl_buffer_create_for_image( + d->shm, size_xy, WL_SHM_FORMAT_ARGB8888, &cursor->custom_data, &cursor->custom_data_size); + if (buffer == nullptr) { return GHOST_kFailure; } - struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size)); - - wl_buffer *buffer = wl_shm_pool_create_buffer( - pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888); - - wl_shm_pool_destroy(pool); - close(fd); - wl_buffer_add_listener(buffer, &cursor_buffer_listener, cursor); static constexpr uint32_t black = 0xFF000000; @@ -3406,7 +3511,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, uint32_t *pixel; for (int y = 0; y < sizey; ++y) { - pixel = &static_cast<uint32_t *>(cursor->file_buffer->data)[y * sizex]; + pixel = &static_cast<uint32_t *>(cursor->custom_data)[y * sizex]; for (int x = 0; x < sizex; ++x) { if ((x % 8) == 0) { datab = *bitmap++; @@ -3445,7 +3550,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap) { cursor_t *cursor = &d->inputs[0]->cursor; - if (cursor->file_buffer->data == nullptr) { + if (cursor->custom_data == nullptr) { return GHOST_kFailure; } if (!cursor->is_custom) { @@ -3458,7 +3563,7 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitma bitmap->hot_spot[0] = cursor->wl_image.hotspot_x; bitmap->hot_spot[1] = cursor->wl_image.hotspot_y; - bitmap->data = (uint8_t *)static_cast<void *>(cursor->file_buffer->data); + bitmap->data = (uint8_t *)static_cast<void *>(cursor->custom_data); return GHOST_kSuccess; } @@ -3549,6 +3654,8 @@ static input_grab_state_t input_grab_state_from_mode(const GHOST_TGrabCursorMode static const char *ghost_wl_output_tag_id = "GHOST-output"; static const char *ghost_wl_surface_tag_id = "GHOST-window"; +static const char *ghost_wl_surface_cursor_pointer_tag_id = "GHOST-cursor-pointer"; +static const char *ghost_wl_surface_cursor_tablet_tag_id = "GHOST-cursor-tablet"; bool ghost_wl_output_own(const struct wl_output *output) { @@ -3560,6 +3667,16 @@ bool ghost_wl_surface_own(const struct wl_surface *surface) return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_tag_id; } +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_pointer_tag_id; +} + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_tablet_tag_id; +} + void ghost_wl_output_tag(struct wl_output *output) { wl_proxy_set_tag((struct wl_proxy *)output, &ghost_wl_output_tag_id); @@ -3570,6 +3687,16 @@ void ghost_wl_surface_tag(struct wl_surface *surface) wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_tag_id); } +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface) +{ + wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_cursor_pointer_tag_id); +} + +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface) +{ + wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_cursor_tablet_tag_id); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 0d51759aa2f..bdf5f2fc273 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -41,6 +41,12 @@ bool ghost_wl_surface_own(const struct wl_surface *surface); void ghost_wl_surface_tag(struct wl_surface *surface); GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *surface); +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface); + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface); + #ifdef WITH_GHOST_WAYLAND_DYNLOAD /** * Return true when all required WAYLAND libraries are present, diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 1365c281ab4..5a930209376 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1256,9 +1256,8 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, - ascii, - utf8_char, - is_repeat); + is_repeat, + utf8_char); // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter } diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 5e549b54afd..00cc5f3af8f 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -745,9 +745,8 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) GHOST_kEventKeyDown, window, ghost_key_from_keysym(modifiers[i]), - '\0', - nullptr, - false)); + false, + nullptr)); } } } @@ -1017,7 +1016,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case KeyRelease: { XKeyEvent *xke = &(xe->xkey); KeySym key_sym; + char *utf8_buf = nullptr; char ascii; + #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* utf8_array[] is initial buffer used for Xutf8LookupString(). * if the length of the utf8 string exceeds this array, allocate @@ -1026,12 +1027,10 @@ void GHOST_SystemX11::processEvent(XEvent *xe) * at the end of this buffer when the constructor of GHOST_EventKey * reads 6 bytes regardless of the effective data length. */ char utf8_array[16 * 6 + 5]; /* 16 utf8 characters */ - char *utf8_buf = utf8_array; - int len = 1; /* at least one null character will be stored */ + int len = 1; /* at least one null character will be stored */ #else - char *utf8_buf = nullptr; + char utf8_array[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; #endif - GHOST_TEventType type = (xke->type == KeyPress) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; GHOST_TKey gkey; @@ -1151,81 +1150,94 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #endif #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) - /* Setting unicode on key-up events gives #XLookupNone status. */ - XIC xic = window->getX11_XIC(); - if (xic && xke->type == KeyPress) { - Status status; + /* Only used for key-press. */ + XIC xic = nullptr; +#endif - /* Use utf8 because its not locale repentant, from XORG docs. */ - if (!(len = Xutf8LookupString( - xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { - utf8_buf[0] = '\0'; - } + if (xke->type == KeyPress) { + utf8_buf = utf8_array; +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + /* Setting unicode on key-up events gives #XLookupNone status. */ + xic = window->getX11_XIC(); + if (xic) { + Status status; + + /* Use utf8 because its not locale repentant, from XORG docs. */ + if (!(len = Xutf8LookupString( + xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { + utf8_buf[0] = '\0'; + } - if (status == XBufferOverflow) { - utf8_buf = (char *)malloc(len + 5); - len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); - } + if (status == XBufferOverflow) { + utf8_buf = (char *)malloc(len + 5); + len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); + } - if (ELEM(status, XLookupChars, XLookupBoth)) { - if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ - /* do nothing for now, this is valid utf8 */ + if (ELEM(status, XLookupChars, XLookupBoth)) { + if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ + /* do nothing for now, this is valid utf8 */ + } + else { + utf8_buf[0] = '\0'; + } + } + else if (status == XLookupKeySym) { + /* this key doesn't have a text representation, it is a command + * key of some sort */ } else { - utf8_buf[0] = '\0'; + printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", + (unsigned int)key_sym, + (status == XLookupNone ? "XLookupNone" : + status == XLookupKeySym ? "XLookupKeySym" : + "Unknown status")); + + printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); } } - else if (status == XLookupKeySym) { - /* this key doesn't have a text representation, it is a command - * key of some sort */ - } else { - printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", - (unsigned int)key_sym, - (status == XLookupNone ? "XLookupNone" : - status == XLookupKeySym ? "XLookupKeySym" : - "Unknown status")); - - printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); + utf8_buf[0] = '\0'; } - } - else { - utf8_buf[0] = '\0'; - } #endif + if (!utf8_buf[0] && ascii) { + utf8_buf[0] = ascii; + utf8_buf[1] = '\0'; + } + } - g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, ascii, utf8_buf, is_repeat); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* when using IM for some languages such as Japanese, * one event inserts multiple utf8 characters */ - if (xic && xke->type == KeyPress) { + if (xke->type == KeyPress && xic) { unsigned char c; int i = 0; - while (1) { - /* search character boundary */ - if ((unsigned char)utf8_buf[i++] > 0x7f) { + while (true) { + /* Search character boundary. */ + if ((uchar)utf8_buf[i++] > 0x7f) { for (; i < len; ++i) { c = utf8_buf[i]; - if (c < 0x80 || c > 0xbf) + if (c < 0x80 || c > 0xbf) { break; + } } } - if (i >= len) + if (i >= len) { break; - - /* enqueue previous character */ + } + /* Enqueue previous character. */ pushEvent(g_event); g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, '\0', &utf8_buf[i], is_repeat); + getMilliSeconds(), type, window, gkey, is_repeat, &utf8_buf[i]); } } - if (utf8_buf != utf8_array) + if (utf8_buf != utf8_array) { free(utf8_buf); + } #endif break; diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp index 19684a44169..e8785cbdb24 100644 --- a/intern/ghost/intern/GHOST_WindowManager.cpp +++ b/intern/ghost/intern/GHOST_WindowManager.cpp @@ -162,17 +162,3 @@ GHOST_IWindow *GHOST_WindowManager::getWindowAssociatedWithOSWindow(void *osWind } return nullptr; } - -bool GHOST_WindowManager::getAnyModifiedState() -{ - bool isAnyModified = false; - std::vector<GHOST_IWindow *>::iterator iter; - - for (iter = m_windows.begin(); iter != m_windows.end(); ++iter) { - if ((*iter)->getModifiedState()) { - isAnyModified = true; - } - } - - return isAnyModified; -} diff --git a/intern/ghost/intern/GHOST_WindowManager.h b/intern/ghost/intern/GHOST_WindowManager.h index 9d20413c433..bf7a0f4ec61 100644 --- a/intern/ghost/intern/GHOST_WindowManager.h +++ b/intern/ghost/intern/GHOST_WindowManager.h @@ -109,12 +109,6 @@ class GHOST_WindowManager { */ GHOST_IWindow *getWindowAssociatedWithOSWindow(void *osWindow); - /** - * Return true if any windows has a modified status - * \return True if any window has unsaved changes - */ - bool getAnyModifiedState(); - protected: /** The list of windows managed */ std::vector<GHOST_IWindow *> m_windows; |