Sorry to resend that patch I think the attachment wasn't in the
previous mail.
Hi,
Well, I finally did it. Here is a very first XIM implementation.
It just works for european languages (dead keys/compose).
I've been late making it because I had to learn the OpenStep API
before digging deeper GNUstep. Don't hesitate mailing me if something
is wrong in it. I just would like to stress that it's a first
incomplete implementation but it do works.
> Yes - there are quite a lot of issues with text management in gnustep.
:-)
Well the bases are here ;o)
>
> My suggestion is to have a look into
>
> NSInputManager / NSInputServer
>
> in the MacOS-X documentation - because there is were the input
managing is
> supposed to be done according to their documentation.
>
> MacOS-X uses a remote InputServer - we should think whether we want to
> have that / whether it can be fit in our environment or not.
>
> The questions we want to ask ourselves are probably -
>
> * what are the advantages of having a remote input server
>
> * what are the disadvantages of having a remote input server
>
> * how would that fit in our environment
>
> I've not yet thought about these issues, so I don't have answers
ready.
I read thorougly the XIM documentation and the gtk XIM implementation,
so let me tell what I think.
X make a huge difference between the input server and the client, that
is to say we just need to code the support of XIM by using the client
API.
So the remote code will be in the X input server, depending on the
client
it could be none (US), integrated in X (european languages) or by an
external
programme (typically asian languages). But I think that each GNUstep
program
need to use a NSInputServer client of XIM which will fire NSEvent rather
than the actual implementation and will communicate with the GUI to make
it compliant with the user Input Method (i.e. make the text entry that
size, make a status bar for the IM, etc). The issue is that it's very
architecture dependant, so the NSInputServer and maybe parts of
NSInputManager
will in xgps/SharedX because it's going to work upong x events, not
NSEvent, is this clear ?
We do not need to follow MacOsX's NSInputManager and especially
NSInputServer
because there are partly architecture-specifique, i.e. in that case
MacOSX-centric.
Remember that X let the user choose a different IM for each program.
>
> > And now the questions :
>
> > - Is there anybody working on this right now ?
>
> not really - but I'm probably the one having done more work on
keyboard
> input up to now
>
> I might take long to answer because I've my hands in too many things
:-)
> please be patient and don't let you down just because I'm sometime
slow in
> answering.
That's OK. I think that if it's OK I'm going to continue working on this
if you're ok.
>
> > - Do I need to do a copyright assignment to the FSF ?
>
> Yes
Let me know what I've got to do.
--
Christian Gillot <[EMAIL PROTECTED]>
GNU/Linux programmer
2001-12-21 Christian Gillot <[EMAIL PROTECTED]>
* Headers/gnustep/xgps/XGContextWindow.h: Add an input context to the
gswindow_device_t structure
* Source/GNUmakefile: Add the xim.c
* Source/GNUmakefile.preamble: Add a -DUSE_XIM in order to compile with
XIM support, to be adapted to the GNUstep model
* Source/SharedX/XGContextEvent.m: Change the key event processing to
support XIM. In process_key_event, move the char and keycode processing
to the very beginning in order to cope with XIM, because in XIM
you may or may not have a keycode.
* Source/SharedX/XGContextWindow.m: Create and close an input context
for the window if XIM was initialized
* Source/SharedX/xim.c: Very first and basic xim implementation
* Source/SharedX/xim.h: Very first and basic xim implementation
* Source/XGContext.m: After the display connection made, do the
XIM initialization and cleanup just before closing the display
diff -r -N -u gnustep-cvs/core/xgps/Headers/gnustep/xgps/XGContextWindow.h gnustep/core/xgps/Headers/gnustep/xgps/XGContextWindow.h
--- gnustep-cvs/core/xgps/Headers/gnustep/xgps/XGContextWindow.h Fri Dec 21 14:16:55 2001
+++ gnustep/core/xgps/Headers/gnustep/xgps/XGContextWindow.h Thu Dec 20 00:18:47 2001
@@ -87,6 +87,9 @@
int boff;
Atom protocols[4];
int numProtocols;
+#ifdef USE_XIM
+ XIC ic;
+#endif
} gswindow_device_t;
@interface XGContext (DPSWindow)
diff -r -N -u gnustep-cvs/core/xgps/Source/GNUmakefile gnustep/core/xgps/Source/GNUmakefile
--- gnustep-cvs/core/xgps/Source/GNUmakefile Fri Dec 21 14:16:56 2001
+++ gnustep/core/xgps/Source/GNUmakefile Thu Dec 20 01:03:49 2001
@@ -58,6 +58,7 @@
SharedX/raster.c \
SharedX/scale.c \
SharedX/xdnd.c \
+SharedX/xim.c \
SharedX/xutil.c \
SharedX/xrtools.c
endif
diff -r -N -u gnustep-cvs/core/xgps/Source/GNUmakefile.preamble gnustep/core/xgps/Source/GNUmakefile.preamble
--- gnustep-cvs/core/xgps/Source/GNUmakefile.preamble Fri Dec 21 14:16:56 2001
+++ gnustep/core/xgps/Source/GNUmakefile.preamble Thu Dec 20 01:04:10 2001
@@ -49,10 +49,10 @@
$(CONFIG_SYSTEM_DEFS)
# Additional flags to pass to the Objective-C compiler
-ADDITIONAL_OBJCFLAGS =
+ADDITIONAL_OBJCFLAGS = -DUSE_XIM
# Additional flags to pass to the C compiler
-ADDITIONAL_CFLAGS =
+ADDITIONAL_CFLAGS = -DUSE_XIM
# Additional include directories the compiler should search
ADDITIONAL_INCLUDE_DIRS = -I../Headers \
diff -r -N -u gnustep-cvs/core/xgps/Source/SharedX/XGContextEvent.m gnustep/core/xgps/Source/SharedX/XGContextEvent.m
--- gnustep-cvs/core/xgps/Source/SharedX/XGContextEvent.m Fri Dec 21 14:16:55 2001
+++ gnustep/core/xgps/Source/SharedX/XGContextEvent.m Fri Dec 21 14:28:52 2001
@@ -50,6 +50,10 @@
#include "SharedX/xrtools.h"
#include "SharedX/xdnd.h"
+#ifdef USE_XIM
+#include "SharedX/xim.h"
+#endif
+
#include "math.h"
#include <X11/keysym.h>
#include <X11/Xproto.h>
@@ -252,6 +256,15 @@
{
//NSDebugLLog(@"NSEvent", @"Get next XWindows event\n");
XNextEvent(XDPY, &xEvent);
+
+#ifdef USE_XIM
+ if (XFilterEvent(&xEvent,xEvent.xkey.window))
+ {
+ NSDebugLLog(@"NSEventI", @"Event filtered by XIM\n");
+ continue;
+ }
+#endif
+
switch (xEvent.type)
{
// mouse button events
@@ -1319,37 +1332,88 @@
int control_key = 0;
int command_key = 0;
int alt_key = 0;
+#ifdef USE_XIM
+ int buf_len = 255;
+ Status status;
+#endif
+ Display *display = [XGContext currentXDisplay];
NSDebugLog(@"Process key event");
if (_is_keyboard_initialized == NO)
initialize_keyboard ();
- /* Process NSFlagsChanged events. We can't use a switch because we
- are not comparing to constants. */
- if (xEvent->xkey.keycode == _control_keycodes[0])
- {
- control_key = 1;
- }
- else if (xEvent->xkey.keycode == _control_keycodes[1])
- {
- control_key = 2;
- }
- else if (xEvent->xkey.keycode == _command_keycodes[0])
+ /* Process location */
+ window = [XGContext _windowWithTag: [[NSApp keyWindow] windowNumber]];
+ eventLocation.x = xEvent->xbutton.x;
+ if (window)
{
- command_key = 1;
+ eventLocation.y = window->siz_hints.height - xEvent->xbutton.y;
}
- else if (xEvent->xkey.keycode == _command_keycodes[1])
+ else
{
- command_key = 2;
+ eventLocation.y = xEvent->xbutton.y;
}
- else if (xEvent->xkey.keycode == _alt_keycodes[0])
+
+ /* Process characters */
+#ifdef USE_XIM
+ if (window->ic)
{
- alt_key = 1;
+ count = XmbLookupString(window->ic, (XKeyEvent *)xEvent,
+ buf, buf_len, &keysym, &status);
+ // FIXME: code this
+ if (status==XBufferOverflow)
+ NSDebugLLog(@"NSEvent",@"XmbLookupString buffer overflow\n");
+ /* Process keycode */
+ keyCode = XKeysymToKeycode(display,keyCode);
+ }
+ else
+ {
+ count = XLookupString ((XKeyEvent *)xEvent, buf, 256, &keysym, &compose);
+ /* Process keycode */
+ keyCode = XKeysymToKeycode(display,keyCode);
}
- else if (xEvent->xkey.keycode == _alt_keycodes[1])
+#else
+ count = XLookupString ((XKeyEvent *)xEvent, buf, 256, &keysym, &compose);
+ /* Process keycode */
+ keyCode = XKeysymToKeycode(display,keyCode);
+#endif
+
+
+ NSDebugLog (@"keysym=%d, xLocation = (%d, %d), userLocation = (%f, %f)",
+ keysym, xEvent->xbutton.x, xEvent->xbutton.y,
+ eventLocation.x, eventLocation.y);
+
+ /* Process NSFlagsChanged events. We can't use a switch because we
+ are not comparing to constants. */
+ // the keyCode is 0 if the input is the result of
+ // an input method
+ if (keyCode)
{
- alt_key = 2;
+ if (keyCode == _control_keycodes[0])
+ {
+ control_key = 1;
+ }
+ else if (keyCode == _control_keycodes[1])
+ {
+ control_key = 2;
+ }
+ else if (keyCode == _command_keycodes[0])
+ {
+ command_key = 1;
+ }
+ else if (keyCode == _command_keycodes[1])
+ {
+ command_key = 2;
+ }
+ else if (keyCode == _alt_keycodes[0])
+ {
+ alt_key = 1;
+ }
+ else if (keyCode == _alt_keycodes[1])
+ {
+ alt_key = 2;
+ }
}
if (control_key || command_key || alt_key)
@@ -1378,27 +1442,6 @@
/* Process modifiers */
eventFlags = process_modifier_flags (xEvent->xkey.state);
- /* Process location */
- window = [XGContext _windowWithTag: [[NSApp keyWindow] windowNumber]];
- eventLocation.x = xEvent->xbutton.x;
- if (window)
- {
- eventLocation.y = window->siz_hints.height - xEvent->xbutton.y;
- }
- else
- {
- eventLocation.y = xEvent->xbutton.y;
- }
-
- NSDebugLog (@"keysym=%d, xLocation = (%d, %d), userLocation = (%f, %f)",
- keysym, xEvent->xbutton.x, xEvent->xbutton.y,
- eventLocation.x, eventLocation.y);
-
- /* Process keycode */
- keyCode = ((XKeyEvent *)xEvent)->keycode;
-
- /* Process characters */
- count = XLookupString ((XKeyEvent *)xEvent, buf, 256, &keysym, &compose);
/* Add NSNumericPadKeyMask if the key is in the KeyPad */
if (IsKeypadKey (keysym))
@@ -1439,7 +1482,20 @@
// Now the same ignoring modifiers, except Shift, ShiftLock, NumLock.
xEvent->xkey.state = (xEvent->xkey.state & (ShiftMask | LockMask
| _num_lock_mask));
+#ifdef USE_XIM
+ if (window->ic)
+ {
+ count = XmbLookupString(window->ic, (XKeyEvent *)xEvent,
+ buf, buf_len, &keysym, &status);
+ // FIXME: code this
+ if (status==XBufferOverflow)
+ NSDebugLLog(@"NSEvent",@"XmbLookupString buffer overflow\n");
+ }
+ else
+ count = XLookupString ((XKeyEvent *)xEvent, buf, 256, &keysym, &compose);
+#else
count = XLookupString ((XKeyEvent *)xEvent, buf, 256, &keysym, &compose);
+#endif
// Make sure that the string is properly terminated
if (count > 255)
@@ -1727,4 +1783,3 @@
}
@end
-
diff -r -N -u gnustep-cvs/core/xgps/Source/SharedX/XGContextWindow.m gnustep/core/xgps/Source/SharedX/XGContextWindow.m
--- gnustep-cvs/core/xgps/Source/SharedX/XGContextWindow.m Fri Dec 21 14:16:55 2001
+++ gnustep/core/xgps/Source/SharedX/XGContextWindow.m Fri Dec 21 14:36:07 2001
@@ -41,6 +41,10 @@
#endif
#include <X11/cursorfont.h>
+#ifdef USE_XIM
+#include "SharedX/xim.h"
+#endif
+
#define MAX_SCREENS 100
#define XDPY (((RContext *)context)->dpy)
#define XDRW (((RContext *)context)->drawable)
@@ -564,6 +568,7 @@
window->xframe = NSMakeRect(x, y, width, height);
NSMapInsert (windowtags, (void*)window->number, window);
NSMapInsert (windowmaps, (void*)window->ident, window);
+
return window;
}
@@ -1036,6 +1041,17 @@
// Insert window into the mapping
NSMapInsert(windowmaps, (void*)window->ident, window);
NSMapInsert(windowtags, (void*)window->number, window);
+
+#ifdef USE_XIM
+ // Create the input context, if it fails
+ // we'll use the bare X input process
+ window->ic = xim_create_ic(XDPY,window->ident);
+
+ if (window->ic==NULL)
+ {
+ xim_close();
+ }
+#endif
}
- (void) DPStermwindow: (int)num
@@ -2052,6 +2068,10 @@
generic.desiredFocusWindow = win;
generic.focusRequestNumber = XNextRequest(XDPY);
XSetInputFocus(XDPY, window->ident, RevertToParent, generic.lastTime);
+#ifdef USE_XIM
+ NSDebugLLog(@"NSEventI", @"XSetICFocus");
+ XSetICFocus(window->ic);
+#endif
}
/*
diff -r -N -u gnustep-cvs/core/xgps/Source/SharedX/xim.c gnustep/core/xgps/Source/SharedX/xim.c
--- gnustep-cvs/core/xgps/Source/SharedX/xim.c Thu Jan 1 01:00:00 1970
+++ gnustep/core/xgps/Source/SharedX/xim.c Fri Dec 21 14:42:38 2001
@@ -0,0 +1,153 @@
+/*
+ xim - X Input Method code for i18n input code for X11 backends.
+
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ Written by: Christian Gillot <[EMAIL PROTECTED]>
+ Date: Nov 2001
+
+ This file is part of the GNU Objective C User Interface Library.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifdef USE_XIM
+
+#include <X11/Xlib.h>
+#include <X11/Xlocale.h>
+
+#include <stdlib.h>
+
+// #define XIM_DEBUG
+#define xim_version_at_least(a,b) ((a) <= (b))
+
+#ifdef XIM_DEBUG
+#define xim_debug(a,b...) printf("%s: %d: " a "\n", __FILE__, __LINE__ , ## b)
+#else
+
+#ifdef NeXT_RUNTIME
+#define xim_debug //
+#else /* !NeXT_RUNTIME */
+#define xim_debug(a,b...)
+#endif /* NeXT_RUNTIME */
+
+#endif
+
+
+#include "xim.h"
+
+static XIM xim;
+static XIMStyle xim_style;
+/* For the only style supported we can only use a global
+context but this is temporary as we'll need to create one
+per text entry */
+XIC global_xic=NULL;
+
+int xim_init(Display *dpy)
+{
+ char *current_locale;
+ int i=0;
+
+ if (!setlocale (LC_ALL,""))
+ xim_debug("locale not supported by C library");
+
+ if (!XSupportsLocale ())
+ {
+ xim_debug("locale not supported by Xlib, locale set to C\n");
+ setlocale (LC_ALL, "C");
+ }
+
+ if (!XSetLocaleModifiers (""))
+ xim_debug("can not set locale modifiers\n");
+
+ current_locale = setlocale (LC_ALL, NULL);
+
+ xim = XOpenIM(dpy,NULL,NULL,NULL);
+ if (xim == NULL) {
+ xim_debug("Can't open XIM.\n");
+ return 0;
+ }
+
+ if (!style_init())
+ {
+ xim_close();
+ return 0;
+ }
+
+ return 1;
+}
+
+static int style_init()
+{
+ /* FIXME: Right now we only support this style *but*
+ this is only temporary */
+ XIMStyle xim_supported_style=XIMPreeditNothing|XIMStatusNothing;
+ XIMStyles *styles;
+ char *failed_arg;
+ int i;
+
+ failed_arg = XGetIMValues(xim,XNQueryInputStyle,&styles,NULL);
+ if (failed_arg!=NULL)
+ {
+ xnd_debug("Can't getting the following IM value :%s",failed_arg);
+ return 0;
+ }
+
+ for (i=0;i<styles->count_styles;i++)
+ {
+ if (styles->supported_styles[i]==xim_supported_style)
+ {
+ xim_style=xim_supported_style;
+ XFree(styles);
+ return 1;
+ }
+ }
+
+
+
+ XFree(styles);
+ return 0;
+}
+
+void xim_close()
+{
+ if (xim)
+ XCloseIM(xim);
+ xim=NULL;
+}
+
+XIC xim_create_ic(Display *dpy, Window w)
+{
+ XIC xic;
+ xic = XCreateIC(xim,XNClientWindow,w,XNInputStyle,xim_style,XNFocusWindow, w,NULL);
+ if (xic==NULL)
+ xim_debug("Can't create the input context.\n");
+ return xic;
+}
+
+unsigned long xim_xic_get_mask(XIC xic)
+{
+ unsigned long xic_xmask = 0;
+ if (XGetICValues(xic,XNFilterEvents,&xic_xmask,NULL)!=NULL)
+ xim_debug("Can't get the event mask for that input context");
+
+ return xic_xmask;
+}
+
+void xim_close_ic(XIC xic)
+{
+ XDestroyIC(xic);
+}
+
+#endif
diff -r -N -u gnustep-cvs/core/xgps/Source/SharedX/xim.h gnustep/core/xgps/Source/SharedX/xim.h
--- gnustep-cvs/core/xgps/Source/SharedX/xim.h Thu Jan 1 01:00:00 1970
+++ gnustep/core/xgps/Source/SharedX/xim.h Fri Nov 30 09:37:34 2001
@@ -0,0 +1,13 @@
+#ifndef __XIM_H__
+#define __XIM_H__
+
+#include <X11/Xlib.h>
+
+int xim_init(Display *dpy);
+static int style_init();
+void xim_close();
+XIC xim_create_ic(Display *dpy, Window w);
+unsigned long xim_xic_get_mask(XIC xic);
+void xim_close_ic(XIC xic);
+
+#endif
diff -r -N -u gnustep-cvs/core/xgps/Source/XGContext.m gnustep/core/xgps/Source/XGContext.m
--- gnustep-cvs/core/xgps/Source/XGContext.m Fri Dec 21 14:16:56 2001
+++ gnustep/core/xgps/Source/XGContext.m Thu Dec 20 01:07:02 2001
@@ -47,6 +47,10 @@
#include <X11/Xutil.h>
#include <X11/keysym.h>
+#ifdef USE_XIM
+#include "SharedX/xim.h"
+extern XIC global_xic;
+#endif
#define GSI_ARRAY_TYPES GSUNION_OBJ
@@ -313,6 +317,11 @@
XSetErrorHandler(XGErrorHandler);
+#ifdef USE_XIM
+ // XIM initialization
+ xim_init(dpy);
+#endif
+
[self _setupRootWindow];
return self;
}
@@ -386,6 +395,9 @@
}
else
{
+#ifdef USE_XIM
+ xim_close();
+#endif
XCloseDisplay(XDPY);
[super dealloc];
}