From 355ca921be898890a44d2094e4ef81f97ca2decf Mon Sep 17 00:00:00 2001 From: DMJC Date: Thu, 21 May 2026 15:52:34 +1000 Subject: [PATCH] wayland: add WaylandInputServer for input method support Implement NSInputServer/NSInputServiceProvider for the Wayland backend. WaylandInputServer tracks the focused window via activeConversationChanged: and satisfies the input-method protocol (inputMethodStyle, fontSize, clientWindowRect, preedit/status area accessors). WaylandServer instantiates and owns the input server, and a new WaylandServer (InputMethod) category delegates all queries to it. Co-Authored-By: Claude Sonnet 4.6 --- ChangeLog | 14 +++ Headers/wayland/WaylandInputServer.h | 54 ++++++++++ Headers/wayland/WaylandServer.h | 18 +++- Source/wayland/GNUmakefile | 1 + Source/wayland/WaylandInputServer.m | 155 +++++++++++++++++++++++++++ Source/wayland/WaylandServer.m | 63 +++++++++++ 6 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 Headers/wayland/WaylandInputServer.h create mode 100644 Source/wayland/WaylandInputServer.m diff --git a/ChangeLog b/ChangeLog index 7ecf0456..8644c6b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2026-05-21 DMJC + + * Headers/wayland/WaylandInputServer.h: + * Source/wayland/WaylandInputServer.m: + * Source/wayland/GNUmakefile: + * Headers/wayland/WaylandServer.h: + * Source/wayland/WaylandServer.m: + Add WaylandInputServer, implementing the NSInputServer/NSInputServiceProvider + protocol for the Wayland backend. WaylandServer instantiates it at startup + and delegates all input-method queries (inputMethodStyle, fontSize, + clientWindowRect, preedit/status area accessors) to it via a new + WaylandServer (InputMethod) category. The focused window is tracked via + activeConversationChanged:. + 2026-05-21 DMJC * configure.ac: diff --git a/Headers/wayland/WaylandInputServer.h b/Headers/wayland/WaylandInputServer.h new file mode 100644 index 00000000..26ae14ac --- /dev/null +++ b/Headers/wayland/WaylandInputServer.h @@ -0,0 +1,54 @@ +/* WaylandInputServer - Keyboard input handling for Wayland backend + + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _WaylandInputServer_h_INCLUDE +#define _WaylandInputServer_h_INCLUDE + +#include + +@interface WaylandInputServer : NSInputServer +{ + id delegate; + NSString *server_name; + int focused_window_id; +} + +- (id) initWithDelegate: (id)aDelegate name: (NSString *)name; +- (void) setFocusedWindowId: (int)windowId; +- (int) focusedWindowId; + +@end + +@interface WaylandInputServer (InputMethod) +- (NSString *) inputMethodStyle; +- (NSString *) fontSize: (int *)size; +- (BOOL) clientWindowRect: (NSRect *)rect; +- (BOOL) statusArea: (NSRect *)rect; +- (BOOL) preeditArea: (NSRect *)rect; +- (BOOL) preeditSpot: (NSPoint *)p; +- (BOOL) setStatusArea: (NSRect *)rect; +- (BOOL) setPreeditArea: (NSRect *)rect; +- (BOOL) setPreeditSpot: (NSPoint *)p; +@end + +#endif /* _WaylandInputServer_h_INCLUDE */ diff --git a/Headers/wayland/WaylandServer.h b/Headers/wayland/WaylandServer.h index f4bbff49..8fc4cb3d 100644 --- a/Headers/wayland/WaylandServer.h +++ b/Headers/wayland/WaylandServer.h @@ -189,12 +189,24 @@ struct window *get_window_with_id(WaylandConfig *wlconfig, int winid); WaylandConfig *wlconfig; BOOL _mouseInitialized; + id inputServer; } @end -@interface -WaylandServer (Cursor) -- (void)initializeMouseIfRequired; +@interface WaylandServer (Cursor) +- (void) initializeMouseIfRequired; +@end + +@interface WaylandServer (InputMethod) +- (NSString *) inputMethodStyle; +- (NSString *) fontSize: (int *)size; +- (BOOL) clientWindowRect: (NSRect *)rect; +- (BOOL) statusArea: (NSRect *)rect; +- (BOOL) preeditArea: (NSRect *)rect; +- (BOOL) preeditSpot: (NSPoint *)p; +- (BOOL) setStatusArea: (NSRect *)rect; +- (BOOL) setPreeditArea: (NSRect *)rect; +- (BOOL) setPreeditSpot: (NSPoint *)p; @end #endif /* _WaylandServer_h_INCLUDE */ diff --git a/Source/wayland/GNUmakefile b/Source/wayland/GNUmakefile index d7d70fef..3a9b764e 100644 --- a/Source/wayland/GNUmakefile +++ b/Source/wayland/GNUmakefile @@ -51,6 +51,7 @@ WaylandServer+Keyboard.m \ WaylandServer+Seat.m \ WaylandServer+Xdgshell.m \ WaylandServer+Layershell.m \ +WaylandInputServer.m \ ifeq ($(WITH_EGL),yes) wayland_OBJC_FILES += \ diff --git a/Source/wayland/WaylandInputServer.m b/Source/wayland/WaylandInputServer.m new file mode 100644 index 00000000..449987da --- /dev/null +++ b/Source/wayland/WaylandInputServer.m @@ -0,0 +1,155 @@ +/* WaylandInputServer - Keyboard input handling for Wayland backend + + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of the GNUstep Backend. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; see the file COPYING.LIB. + If not, see or write to the + Free Software Foundation, 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "wayland/WaylandInputServer.h" + +@implementation WaylandInputServer + +- (id) initWithDelegate: (id)aDelegate name: (NSString *)name +{ + delegate = aDelegate; + ASSIGN(server_name, name); + focused_window_id = 0; + NSDebugLog(@"WaylandInputServer: initialized"); + return self; +} + +- (void) dealloc +{ + DESTROY(server_name); + [super dealloc]; +} + +- (void) setFocusedWindowId: (int)windowId +{ + focused_window_id = windowId; + NSDebugLog(@"WaylandInputServer: focused window id = %d", windowId); +} + +- (int) focusedWindowId +{ + return focused_window_id; +} + +/* NSInputServiceProvider protocol */ + +- (void) activeConversationChanged: (id)sender + toNewConversation: (long)newConversation +{ + [super activeConversationChanged: sender toNewConversation: newConversation]; + + if ([sender respondsToSelector: @selector(window)] == NO) + return; + + /* sender is a text client; -window is checked via respondsToSelector above */ + NSWindow *window = [sender performSelector: @selector(window)]; + if (window != nil) + { + focused_window_id = [window windowNumber]; + NSDebugLog(@"WaylandInputServer: conversation changed, focused window = %d", + focused_window_id); + } +} + +- (void) activeConversationWillChange: (id)sender + fromOldConversation: (long)oldConversation +{ + [super activeConversationWillChange: sender + fromOldConversation: oldConversation]; +} + +@end + +@implementation WaylandInputServer (InputMethod) + +- (NSString *) inputMethodStyle +{ + /* Wayland keyboard input is handled directly via XKB in WaylandServer+Keyboard.m. + No input method overlay is used. */ + return nil; +} + +- (NSString *) fontSize: (int *)size +{ + NSString *str; + + str = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSFontSize"]; + if (!str) + str = @"12"; + if (size) + *size = (int) strtol([str cString], NULL, 10); + return str; +} + +- (BOOL) clientWindowRect: (NSRect *)rect +{ + if (!rect || focused_window_id == 0) + return NO; + NSWindow *window = GSWindowWithNumber(focused_window_id); + if (window == nil) + return NO; + *rect = [window frame]; + return YES; +} + +- (BOOL) statusArea: (NSRect *)rect +{ + return NO; +} + +- (BOOL) preeditArea: (NSRect *)rect +{ + return NO; +} + +- (BOOL) preeditSpot: (NSPoint *)p +{ + return NO; +} + +- (BOOL) setStatusArea: (NSRect *)rect +{ + return NO; +} + +- (BOOL) setPreeditArea: (NSRect *)rect +{ + return NO; +} + +- (BOOL) setPreeditSpot: (NSPoint *)p +{ + return NO; +} + +@end diff --git a/Source/wayland/WaylandServer.m b/Source/wayland/WaylandServer.m index d66289df..7d57d742 100644 --- a/Source/wayland/WaylandServer.m +++ b/Source/wayland/WaylandServer.m @@ -46,6 +46,7 @@ #include #include "wayland/WaylandServer.h" +#include "wayland/WaylandInputServer.h" #ifdef HAVE_EGL #include "wayland/WaylandOpenGL.h" #endif @@ -228,6 +229,9 @@ - (id)_initWaylandContext @"compositor must support the stable XDG Shell protocol"]; } + inputServer = [[WaylandInputServer allocWithZone: [self zone]] + initWithDelegate: nil name: @"WaylandInput"]; + return self; } @@ -275,6 +279,7 @@ - (id)initWithAttributes:(NSDictionary *)info - (void)dealloc { NSDebugLog(@"Destroying Wayland Server"); + DESTROY(inputServer); [super dealloc]; } @@ -368,6 +373,64 @@ - (void)beep @end +@implementation WaylandServer (InputMethod) + +- (NSString *) inputMethodStyle +{ + return inputServer + ? [(WaylandInputServer *) inputServer inputMethodStyle] : nil; +} + +- (NSString *) fontSize: (int *)size +{ + return inputServer + ? [(WaylandInputServer *) inputServer fontSize: size] : nil; +} + +- (BOOL) clientWindowRect: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer clientWindowRect: rect] : NO; +} + +- (BOOL) statusArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer statusArea: rect] : NO; +} + +- (BOOL) preeditArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer preeditArea: rect] : NO; +} + +- (BOOL) preeditSpot: (NSPoint *)p +{ + return inputServer + ? [(WaylandInputServer *) inputServer preeditSpot: p] : NO; +} + +- (BOOL) setStatusArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer setStatusArea: rect] : NO; +} + +- (BOOL) setPreeditArea: (NSRect *)rect +{ + return inputServer + ? [(WaylandInputServer *) inputServer setPreeditArea: rect] : NO; +} + +- (BOOL) setPreeditSpot: (NSPoint *)p +{ + return inputServer + ? [(WaylandInputServer *) inputServer setPreeditSpot: p] : NO; +} + +@end + @implementation WaylandServer (WindowOps)