Git Product home page Git Product logo

Comments (20)

russellallen avatar russellallen commented on July 27, 2024

Displaying unicode is very complicated :( The open source world seems to be relying mostly on the Harfbuzz/Pango which aren't simple (!) and which would need quite a bit of work to integrate into Self.

What is your native language? Which characters do you need apart from ASCII?

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

What is your native language?

Czech language.

Which characters do you need apart from ASCII?

"á", "é", "ě", "í", "ó", "ú", "ů", "ý", "č", "ď", "ň", "ř", "š", "ť", "ž" and "Á", "É", "Ě", "Í", "Ó", "Ú", "Ů", "Ý", "Č", "Ď", "Ň", "Ř", "Š", "Ť", "Ž".

from self.

davidungar avatar davidungar commented on July 27, 2024

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

I've offered to pay ~$500 to one C++ programmer I know to implement Xlib unicode input. He looked at Self and played with it for a while, but then decided, that he can't do it. He sent me this patch file.

I have no idea whether it is useful for something, but posting anyway for documentation purposes and maybe someone will use it.

diff --git a/vm/cmake/dependencies.cmake b/vm/cmake/dependencies.cmake
index 67e8535..7f17ac0 100644
--- a/vm/cmake/dependencies.cmake
+++ b/vm/cmake/dependencies.cmake
@@ -29,10 +29,12 @@ if(SELF_X11)
       endif()
       set(X11_INCLUDE_DIRS ${X11_INCLUDE_DIR})
     endif()
+
+   pkg_check_modules(XFT REQUIRED xft)
     
-    link_directories(${X11_LIBRARY_DIRS})
-    include_directories(${X11_INCLUDE_DIRS})
-    list(APPEND 3RD_PARTY_LIBS ${X11_LIBRARIES})     
+    link_directories(${X11_LIBRARY_DIRS} ${XFT_LIBRARY_DIRS})
+    include_directories(${X11_INCLUDE_DIRS} ${XFT_INCLUDE_DIRS})
+    list(APPEND 3RD_PARTY_LIBS ${X11_LIBRARIES} ${XFT_LIBRARIES})     
 endif()
 
 if(PKG_CONFIG_FOUND)
diff --git a/vm/src/any/os/xlibWindow.cpp b/vm/src/any/os/xlibWindow.cpp
index 2abf4d2..f23df24 100644
--- a/vm/src/any/os/xlibWindow.cpp
+++ b/vm/src/any/os/xlibWindow.cpp
@@ -16,8 +16,10 @@
 
 
 XPlatformWindow::XPlatformWindow() : AbstractPlatformWindow() {
-  _display   = NULL;
-  _font_info = NULL;
+  _display = NULL;
+  _gc = NULL;
+  _xft_drawable = NULL;
+  _xft_font = NULL;
 }
 
 
@@ -27,45 +29,47 @@ bool XPlatformWindow::open( const char* display_name,
                             int x, int y, int w, int h,
                             int min_w, int max_w, int min_h, int max_h, // -1 for don't care
                             const char* window_name,  const char* icon_name,
-                            const char* font_name,    int   /*font_size unimp X*/ ) {
+                            const char* font_name, int font_size) {
 
   // (adapted from Spy open routine, needs font info for sizing)
   // XOpenDisplay fails silently if a signal is received during the call.
-  // All signals except user interrupts are therefore blocked. 
+  // All signals except user interrupts are therefore blocked.
   SignalBlocker sb(SignalBlocker::allow_user_int);
-  
+
   if (!open_xdisplay(display_name)) { close();  return false; }
-  
+
   bool debugMe = false;              // set to true when debugging X
-  XSynchronize(_display, debugMe);  
+  XSynchronize(_display, debugMe);
   XSetErrorHandler(XErrorHandlers::handle_X_error);
 
   _screen_num     = DefaultScreen(_display);
+  _colormap       = DefaultColormap(_display, _screen_num);
   _display_width  = DisplayWidth (_display, _screen_num);
   _display_height = DisplayHeight(_display, _screen_num);
-  
+
   _window_x = x;  _window_y = y;  _width = w;  _height = h;
 
   // create the window; will be resized and repositioned when reparented
   Window root = RootWindow(_display, _screen_num);
-  
-  _xwindow = XCreateSimpleWindow(  _display, root,
-                                   _window_x, _window_y,
-                                   width(), height(), 0,
-                                   BlackPixel(_display, _screen_num),
-                                   WhitePixel(_display, _screen_num));
-  
-  if (!set_font_info(font_name))                       {  close();  return false; }
-  if (!change_size_hints(min_w, max_w, min_h, max_h))  {  close();  return false; }
-  
+
+  _xwindow = XCreateSimpleWindow(_display, root,
+                                 _window_x, _window_y,
+                                 width(), height(), 0,
+                                 BlackPixel(_display, _screen_num),
+                                 WhitePixel(_display, _screen_num));
+
+  if (!setup_xft_drawable())                           { close();  return false; }
+  if (!set_font_info(font_name, font_size))            { close();  return false; }
+  if (!change_size_hints(min_w, max_w, min_h, max_h))  { close();  return false; }
+
   if (!set_name(     window_name))  {  close(); return false; }
   if (!set_icon_name(  icon_name))  {  close(); return false; }
-  
-  setup_events();  
+
+  setup_events();
   XMapWindow(_display, _xwindow);
-  
+
  if (!setup_gcs())      {  close(); return false; }
- 
+
   return true;
 }
 
@@ -82,8 +86,8 @@ bool XPlatformWindow::open_xdisplay(const char *n) {
   return true;
 }
 
-  
-bool XPlatformWindow::set_icon_name(const char* icon_name) {  
+
+bool XPlatformWindow::set_icon_name(const char* icon_name) {
   XTextProperty iconName;
   if (XStringListToTextProperty((char**)&icon_name, 1, &iconName) == 0) {
     warning("X structure allocation for icon name failed--window won't work.");
@@ -105,12 +109,12 @@ bool XPlatformWindow::set_name(const char* window_name) {
 }
 
 
-void XPlatformWindow::setup_events() {  
+void XPlatformWindow::setup_events() {
   // to catch the clientMessage event when user deletes
   _wmProtocolsAtom    = XInternAtom(_display, "WM_PROTOCOLS",     false);
   _wmDeleteWindowAtom = XInternAtom(_display, "WM_DELETE_WINDOW", false);
   XSetWMProtocols(_display, _xwindow, &_wmDeleteWindowAtom, 1);
-  
+
   // choose events to receive
   long event_mask = ExposureMask | StructureNotifyMask;
   XSelectInput(_display, _xwindow, event_mask);
@@ -120,7 +124,7 @@ void XPlatformWindow::setup_events() {
 bool XPlatformWindow::tell_platform_size_hints() {
   if ( _min_w == -1)
     return true; // hack for don't care
-    
+
   // tell window manager that we'd like our own size and position
   XSizeHints* size_hints;
   if ((size_hints = XAllocSizeHints()) == NULL) {
@@ -138,10 +142,13 @@ bool XPlatformWindow::tell_platform_size_hints() {
 }
 
 
-bool XPlatformWindow::set_font_info(const char* font_name) {  
-  // must set _font_info here so that font_height() is defined for BOTTOM 
-  _font_info = XLoadQueryFont(_display, font_name);
-  return _font_info != NULL;
+bool XPlatformWindow::set_font_info(const char* font_name, int size) {
+  // must set _xft_font here so that font_height() is defined for BOTTOM
+  _xft_font = XftFontOpen(_display, _screen_num,
+                          XFT_FAMILY, XftTypeString, font_name,
+                          XFT_SIZE, XftTypeDouble, double(size),
+                          NULL);
+  return _xft_font != NULL;
 }
 
 
@@ -150,11 +157,10 @@ bool XPlatformWindow::setup_gcs() {
   unsigned long valuemask = 0;
   XGCValues values;
   _gc = XCreateGC(_display, _xwindow, valuemask, &values);
-  
-  XSetFont(_display, _gc, _font_info->fid);
+
   XSetForeground(_display, _gc, BlackPixel(_display, _screen_num));
   XSetBackground(_display, _gc, WhitePixel(_display, _screen_num));
-  
+
   // 16x16 grey stipple pixmap (16x16 is preferred stipple size)
   const int grey_width = 16;
   const int grey_height = 16;
@@ -165,28 +171,35 @@ bool XPlatformWindow::setup_gcs() {
   Pixmap stipple = XCreateBitmapFromData(_display, _xwindow, grey_bits,
                                          grey_width, grey_height);
   XSetStipple(_display, _gc, stipple);
-  
+
   _black= BlackPixel(_display, _screen_num);
   _white= WhitePixel(_display, _screen_num);
   _is_mono = DefaultDepth(_display, _screen_num) == 1;
   if (_is_mono)
     _red= _yellow= _gray= _black;
   else {
-    Colormap cmap= DefaultColormap(_display, _screen_num);
     XColor col1, col2;
-    _red= XAllocNamedColor(_display, cmap, "red", &col1, &col2)
+    _red= XAllocNamedColor(_display, _colormap, "red", &col1, &col2)
       ? col1.pixel : _black;
-    _yellow= XAllocNamedColor(_display, cmap, "gold", &col1, &col2)
+    _yellow= XAllocNamedColor(_display, _colormap, "gold", &col1, &col2)
       ? col1.pixel : _black;
-    _gray= XAllocNamedColor(_display, cmap, "gray", &col1, &col2)
+    _gray= XAllocNamedColor(_display, _colormap, "gray", &col1, &col2)
       ? col1.pixel : _black;
   }
   return true;
 }
 
 
-void XPlatformWindow::close() { 
-  if (_font_info != NULL) { XFreeFont(_display, _font_info); _font_info = NULL; }
+void XPlatformWindow::close() {
+  if (_xft_font) {
+    XftFontClose(_display, _xft_font);
+    _xft_font = NULL;
+  }
+  if (_xft_drawable) {
+    XftColorFree(_display, DefaultVisual(_display, _screen_num), _colormap, &_foreground_xft_color);
+    XftDrawDestroy(_xft_drawable);
+    _xft_drawable = NULL;
+  }
   if (_gc        != NULL) { XFreeGC(  _display, _gc);               _gc = NULL; }
   if (_display   != NULL) { XCloseDisplay(_display);           _display = NULL; }
 }
@@ -213,17 +226,17 @@ int  XPlatformWindow::height() { return _height; }
 int  XPlatformWindow::screen_width()   { return _display_width; }
 int  XPlatformWindow::screen_height()  { return _display_height; }
 int  XPlatformWindow::menubar_height() { return 0; } // none in X
-  
-int  XPlatformWindow::font_width() { return _font_info->max_bounds.width; }
-int  XPlatformWindow::font_height() { return _font_info->max_bounds.ascent + _font_info->max_bounds.descent; }
 
-const char* XPlatformWindow::default_fixed_font_name() { return "fixed"; }
+int  XPlatformWindow::font_width() { return _xft_font->max_advance_width; }
+int  XPlatformWindow::font_height() { return _xft_font->height; }
+
+const char* XPlatformWindow::default_fixed_font_name() { return "monospace"; }
 int   XPlatformWindow::default_fixed_font_size() { return 10; }
 
 
 // Handy operations;
 
-bool XPlatformWindow::change_extent(int left, int top, int w, int h) { 
+bool XPlatformWindow::change_extent(int left, int top, int w, int h) {
   // left, top in global coordinates
   // don't need to adjust by insets for X
   XMoveResizeWindow(_display, _xwindow, left, top, w, h);
@@ -233,7 +246,7 @@ bool XPlatformWindow::change_extent(int left, int top, int w, int h) {
   _height = h;
   return true;
 }
-void XPlatformWindow::adjust_after_resize() { 
+void XPlatformWindow::adjust_after_resize() {
   if (TheSpy != NULL)
     TheSpy->adjust_after_resize(); // in case this is the spy
 }
@@ -255,7 +268,12 @@ bool XPlatformWindow:: pre_draw( bool incremental) {
 void XPlatformWindow::post_draw( bool ) { /* XFlush(_display); should be needed but was not there before */ }
 
 void XPlatformWindow::draw_text(const char* text, int x, int y) {
-  XDrawImageString(_display, _xwindow, _gc, x, y, text, strlen(text));
+  assert(_xft_drawable);
+  assert(_xft_font);
+
+  XftDrawStringUtf8(_xft_drawable, &_foreground_xft_color,
+                    _xft_font, x, y, (FcChar8*)text,
+                    strlen(text));
 }
 
 void XPlatformWindow::draw_line(int x1, int y1, int x2, int y2) {
@@ -266,7 +284,7 @@ void XPlatformWindow::draw_rectangle_black(int x, int y, int w, int h) {
   if (w > 0 && h > 0)
     XDrawRectangle(_display, _xwindow, _gc, x, y, w, h);
 }
-      
+
 
 void XPlatformWindow::clear_rectangle(int x, int y, int w, int h) {
   if (w > 0 && h > 0)
@@ -275,23 +293,82 @@ void XPlatformWindow::clear_rectangle(int x, int y, int w, int h) {
 
 
 // X drawing functions
-// the X calls do the wrong thing if w or h is 0, so suppress these calls 
+// the X calls do the wrong thing if w or h is 0, so suppress these calls
 
 void XPlatformWindow::fill_rectangle(int x, int y, int w, int h) {
   if (w > 0 && h > 0)
     XFillRectangle(_display, _xwindow, _gc, x, y, w, h);
 }
 
-void XPlatformWindow::set_color(int c)     { XSetForeground    (_display, _gc, c); }
+void XPlatformWindow::set_color(int c) {
+  XSetForeground(_display, _gc, c);
+  set_xft_color_from_pixel(c);
+}
+
 void XPlatformWindow::set_thickness(int t) { XSetLineAttributes(_display, _gc, t, LineSolid, CapButt, JoinMiter); }
 void XPlatformWindow::set_xor()            { XSetFunction      (_display, _gc, GXxor);  }
 void XPlatformWindow::set_copy()           { XSetFunction      (_display, _gc, GXcopy); }
 
+bool XPlatformWindow::setup_xft_drawable()
+{
+  _foreground_render_color.red = 0;
+  _foreground_render_color.green = 0;
+  _foreground_render_color.blue = 0;
+  _foreground_render_color.alpha = 65535u;
+
+  if (!XftColorAllocValue(_display,
+                          DefaultVisual(_display, _screen_num),
+                          _colormap,
+                          &_foreground_render_color,
+                          &_foreground_xft_color))
+  {
+    return false;
+  }
+
+  _xft_drawable = XftDrawCreate(_display,
+                                _xwindow,
+                                DefaultVisual(_display, _screen_num),
+                                _colormap);
+  if (!_xft_drawable)
+  {
+    XftColorFree(_display, DefaultVisual(_display, _screen_num), _colormap, &_foreground_xft_color);
+    return false;
+  }
 
-bool XPlatformWindow::get_graphics_semaphore() { 
+  return true;
+}
+
+void XPlatformWindow::set_xft_color_from_pixel(unsigned long pixel)
+{
+  XColor xcolor = {0};
+  xcolor.pixel = pixel;
+  xcolor.flags = DoRed | DoGreen | DoBlue;
+  XQueryColor(_display, _colormap, &xcolor);
+
+  XRenderColor renderColor;
+  renderColor.red = xcolor.red;
+  renderColor.green = xcolor.green;
+  renderColor.blue = xcolor.blue;
+  renderColor.alpha = 65535u;
+
+  XftColor xftColor;
+
+  if (XftColorAllocValue(_display,
+                         DefaultVisual(_display, _screen_num),
+                         _colormap,
+                         &renderColor,
+                         &xftColor))
+  {
+    XftColorFree(_display, DefaultVisual(_display, _screen_num), _colormap, &_foreground_xft_color);
+    _foreground_render_color = renderColor;
+    _foreground_xft_color = xftColor;
+  }
+}
+
+bool XPlatformWindow::get_graphics_semaphore() {
   // if you draw while X may be drawing something else, check this
   // and don't draw if it is true
-  extern bool xlib_semaphore; 
+  extern bool xlib_semaphore;
   return xlib_semaphore;
 }
 
@@ -306,22 +383,22 @@ bool XPlatformWindow::handle_polled_events() {
   while ( XPending(_display) > 0 ) {
     XNextEvent(_display, &event);
     switch (event.type) {
-    
+
      case Expose:
       if (event.xexpose.count != 0) break;
       full_redraw(); // force redraw
       break;
-      
+
      case ConfigureNotify:
       _width  = event.xconfigure.width;
       _height = event.xconfigure.height;
       adjust_after_resize();
       break;
-      
-     case ReparentNotify: 
+
+     case ReparentNotify:
       handle_reparent_event(event);
       break;
-      
+
      case ClientMessage:
       if ((event.xclient.message_type = _wmProtocolsAtom)
           && (event.xclient.data.l[0] = _wmDeleteWindowAtom)) {
@@ -330,7 +407,7 @@ bool XPlatformWindow::handle_polled_events() {
         return false;
       }
       break;
-      
+
      default:
       break;
     }
@@ -344,13 +421,13 @@ void XPlatformWindow::handle_reparent_event(XEvent& event) {
   int x, y;
   unsigned w, h, border_width, depth;
   if (XGetGeometry(_display, event.xreparent.parent, &root, &x, &y,
-                   &w, &h, &border_width, &depth) == 0) 
+                   &w, &h, &border_width, &depth) == 0)
     return;
   int wdelta = w - width();
   // sanity check: sometimes X gives weird width
   if (wdelta < 0) {
     if (XGetGeometry(_display, event.xreparent.parent, &root, &x, &y,
-                     &w, &h, &border_width, &depth) == 0) 
+                     &w, &h, &border_width, &depth) == 0)
       return;
     wdelta = w - width();
     if (wdelta < 0) {
diff --git a/vm/src/any/os/xlibWindow.hh b/vm/src/any/os/xlibWindow.hh
index 396bf5a..a6c43d0 100644
--- a/vm/src/any/os/xlibWindow.hh
+++ b/vm/src/any/os/xlibWindow.hh
@@ -20,11 +20,15 @@ class XPlatformWindow: public AbstractPlatformWindow {
   int           _screen_num;
   int           _display_width, _display_height;
   Window        _xwindow;
+  XftDraw*      _xft_drawable;
+  Colormap      _colormap;
   int           _window_x, _window_y;
-  int           _width,  _height;  
-  XFontStruct*  _font_info;  
+  int           _width,  _height;
+  XftFont*      _xft_font;
   Atom          _wmProtocolsAtom, _wmDeleteWindowAtom;
   GC            _gc;
+  XRenderColor  _foreground_render_color;
+  XftColor      _foreground_xft_color;
   bool          _is_mono;
 
  public:
@@ -90,11 +94,14 @@ class XPlatformWindow: public AbstractPlatformWindow {
   // Open helpers
   bool  open_xdisplay(const char*);
   void  setup_events();
+  bool  setup_xft_drawable();
   bool  set_name(const char*);
   bool  set_icon_name(const char*);
-  bool  set_font_info(const char*);
+  bool  set_font_info(const char*, int size);
   bool  setup_gcs();
   
+  void set_xft_color_from_pixel(unsigned long pixel);
+  
   // Drawing helpers
   bool    get_graphics_semaphore(); // X is not reentrant
     
diff --git a/vm/src/unix/prims/x_includes.hh b/vm/src/unix/prims/x_includes.hh
index 9349ee8..06db282 100644
--- a/vm/src/unix/prims/x_includes.hh
+++ b/vm/src/unix/prims/x_includes.hh
@@ -12,6 +12,7 @@
 # define Cursor SelfX11Cursor // prevent clash with Carbon
 # include <X11/Xlib.h>
 # include <X11/Xutil.h>
+# include <X11/Xft/Xft.h>
 # undef Cursor
 # if TARGET_OS_VERSION == MACOSX_VERSION
 #     undef Status

from self.

doublec avatar doublec commented on July 27, 2024

Back when I looked at Linux fonts and Xft I think I tried XPlatformWindow too, but it wasn't where the font code was being done. It's done in Self. I wrote a post on how to do Xft fonts from Self with the code in this branch. It adds the primitives to enable displaying fonts but doesn't hook it into the existing font code. You'd probably need to follow how the Mac OS Self code does this as it also uses a different font mechanism.

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

I will have to look how is input handled. I suspect, that there may be just some condition that prevents input of any character that self doesn't understand. In the terminal, everything works as expected:

"Self 1" 'č'
'\xc4\x8d'

from self.

russellallen avatar russellallen commented on July 27, 2024

"Self 5" 'ç' printLine ç '\xc3\xa7'

printing to console works too.

To do this right shouldn't we transition to a utf8 string type - detach traits string from traits byteVector and make it standalone to handle multibyte characters etc?

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

To do this right shouldn't we transition to a utf8 string type - detach traits string from traits byteVector and make it standalone to handle multibyte characters etc?

That would be ideal, but I think that input and output has higher priority.

UTF API support is nice thing to have, but you can work around it, if you don't (for example - search for substring, instead of single character). Missing UTF input means, that you literally can't type into the application you are trying to build, which makes it unusable.

I was again looking into the X wrappers yesterday, but the more I see, the less I understand how it actually works. I was talking about it with few C++/Unix programmers, but they all run away when they've seen primitive maker and how the bindings are connected 😞

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

I've managed to trace the input into the traits abstractUI2Event editorKeyCapComboHandler, where is code for handling the input like handlePressWithNoModifiers: combo IfCannot: b, which is limited only to printable characters by this line:

combo nonmodifierKeyCap isPrintable ifTrue: [^combo nonmodifierKeyCap printString do: [|:c| insert_char: c]].

isPrintable is message from string and it just checks whether the character is > 32 && < 128, which limits the input for characters with higher code. When I allowed higher codes (by changing isPrintable implementation), it is now possible to enter ýáíéú (and they also display correctly!), but not ěščřžů or any combining keys (' + a = á, ˇ + d = ď and so on). I am not yet sure why. What I find really strange that using autocutsel, I can copy the written characters out of Self, but when I try to paste them back, garbage occurs.

I am not sure, how is handlePressWithNoModifiers: combo IfCannot: b connected with general Self input and whether this code is just for the editor, or all input. Probably for all input from what I can read from sendMessageToHandleKeyboardEventTo:.

I will need to play with the input system a little bit more to understand how it would be possible to allow any input.

Note to my future self: Is it possible to define combo to include unicode character?

snimek obrazovky porizeny 25 04 2017 00 09 54

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

I've played with Self again today and managed to trace the input handling into the:

  • traits abstractUI2Event editorKeyCapComboHandler where is handlePressWithNoModifiers: combo IfCannot: b

This may be updated to accept any character input by changing:

combo nonmodifierKeyCap isPrintable ifTrue: [^combo nonmodifierKeyCap printString do: [|:c| insert_char: c]].

to

combo nonmodifierKeyCap printString size <= 6 ifTrue: [^combo nonmodifierKeyCap printString do: [|:c| insert_char: c]].

That is the easier part, which will allow input such as éíáý (ISO 8859-1 btw), as I discovered yesterday.

The harder part is keyboard event handling in traits ui2XEvent, specifically keySymFrom: xEvt, which is defined as:

| k |
k: xEvt lookupKeySym.

k =  xEvt xk_Left     ifTrue: [ ^ keyCaps arrows  left  ].
k =  xEvt xk_Up       ifTrue: [ ^ keyCaps arrows  up    ].
k =  xEvt xk_Right    ifTrue: [ ^ keyCaps arrows  right ].
k =  xEvt xk_Down     ifTrue: [ ^ keyCaps arrows  down  ].

k =  xEvt xk_KP_Left   ifTrue: [ ^ keyCaps arrows  left  ].
k =  xEvt xk_KP_Up     ifTrue: [ ^ keyCaps arrows  up    ].
k =  xEvt xk_KP_Right  ifTrue: [ ^ keyCaps arrows  right ].
k =  xEvt xk_KP_Down   ifTrue: [ ^ keyCaps arrows  down  ].

k =  xEvt xk_Shift_L    ifTrue: [ ^ keyCaps oddballs shift   ].
k =  xEvt xk_Shift_R    ifTrue: [ ^ keyCaps oddballs shift   ].
k =  xEvt xk_Control_L  ifTrue: [ ^ keyCaps oddballs control ].
k =  xEvt xk_Control_R  ifTrue: [ ^ keyCaps oddballs control ].
k =  xEvt xk_Alt_L      ifTrue: [ ^ keyCaps oddballs alt     ].
k =  xEvt xk_Alt_R      ifTrue: [ ^ keyCaps oddballs alt     ].
k =  xEvt xk_Super_L    ifTrue: [ ^ keyCaps oddballs command ].
k =  xEvt xk_Super_R    ifTrue: [ ^ keyCaps oddballs command ].

keyCaps unknown

This is translator from unrecognized key codes to appropriate characters / strings.

By inserting

k = 488 ifTrue: [^ keyCaps printableCharacter copy character: '\xa9'].

before keyCaps unknown, I was able to map č key (keysym 0x1e8, =488) to '\xa9' ISO 8859-1 character (© mark). It is also possible to enter unicode strings such as '\xc4\x8d', but they will of course display itself as garbage (Ä⬚), because they are interpreted as ISO 8859-1.

Important point is, that translation from keysym to strings may be done here. I've tried xEvt lookupString, which may be used to resolve some of the other keys, but it returns empty strings for keys like č.

Now, the question is whether there is any possibility for mapping the keysyms (or keycode (=13), both are in xEvt object) to proper unicode representations automatically by calling some X function and half of the work is done.

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

I was thinking about implementing translation table for keysyms in Self, but decided I will try to do it in C++ Xlib code, because I don't want to replicate functionality, which is already there.

I am able to get UTF-8 input for almost* all keys, by modification of https://github.com/russellallen/self/blob/master/objects/glue/xlib_glue.cpp#L448:

from:

int XLookupString_wrap(XKeyEvent* event, char* string, int len,
                       objVectorOop keySym) {
  KeySym ks;
  int n = XLookupString(event, string, len, &ks, NULL);
  if (keySym->length() >= 1) keySym->obj_at_put(0, as_smiOop(ks), false);
  return n;
}

to:

XIC getXICContext(XKeyEvent* event){
  XIM im = XOpenIM(event->display, NULL, NULL, NULL);
  XIC ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, event->window, NULL);
  XSetICFocus(ic);
  return ic;
}

int XLookupString_wrap(XKeyEvent* event, char* string, int len,
                       objVectorOop keySym) {
  KeySym ks;
  int n = Xutf8LookupString(getXICContext(event), event, string, len, &ks, NULL);
  if (keySym->length() >= 1)
    keySym->obj_at_put(0, as_smiOop(ks), false);
  return n;
}

It is basically just using Xutf8LookupString instead of XLookupString. It works nicely for keys like á.

*Except combining / dead keys (´ + a = á). They should somehow work with XFilterEvent, but I wasn't able to make it work. I've tried to modify XNextEvent_wrap from:

oop XNextEvent_wrap(Display *display, bool peek,
                    objVectorOop eventProtos, void *FH) {
  XEvent *evt = new XEvent();
  if (peek)
    XPeekEvent(display, evt);
  else
    XNextEvent(display, evt);
  int type = evt->type;
  if (type < 0 || type >= eventProtos->length()) {
    char err[50];
    sprintf(err, "unknown X event, type = %d", type);
    failure(FH, err);
    delete evt;
    return NULL;
  }
  oop proto = eventProtos->obj_at(type);
  if (!proto->is_proxy()) {
    prim_failure(FH, BADTYPEERROR);
    delete evt;
    return NULL;
  }
  proxyOop res = proxyOop(proto)->clone();
  res->set_pointer(evt);
  res->set_type_seal(XEvent_seal);
  return res;
}

to:

oop XNextEvent_wrap(Display *display, bool peek,
                    objVectorOop eventProtos, void *FH) {
  XEvent *evt = new XEvent();
  if (peek)
    XPeekEvent(display, evt);
  else
    XNextEvent(display, evt);

  if (!peek){
    int revert_to;
    Window active_win;
    XGetInputFocus(display, &active_win, &revert_to); 
    if (XFilterEvent(evt, active_win)){
      //delete evt;
      return NULL;
    }
  }

  int type = evt->type;
  if (type < 0 || type >= eventProtos->length()) {
    char err[50];
    sprintf(err, "unknown X event, type = %d", type);
    failure(FH, err);
    delete evt;
    return NULL;
  }
  oop proto = eventProtos->obj_at(type);
  if (!proto->is_proxy()) {
    prim_failure(FH, BADTYPEERROR);
    delete evt;
    return NULL;
  }
  proxyOop res = proxyOop(proto)->clone();
  res->set_pointer(evt);
  res->set_type_seal(XEvent_seal);
  return res;
}

Which generates 0 event, which I fixed by ignoring such events. But still, it doesn't work. When I press the dead key for ´ and then quickly some other key (a for example), it generates garbage, which changes randomly. If I press other key after a while, it just adds the other key.

From „debug printouts“, I can say that it looks like XLookupString_wrap() is called multiple times, or maybe ´ event is still registered, even if it should be ignored.

I don't really understand how Xlib / Self wrapper works, so maybe I am doing something obviously wrong.

from self.

doublec avatar doublec commented on July 27, 2024

Could it be related to the peek event handling? Maybe filter on peek too?

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

I've tried that, but nope, not related. I am kinda confused by that random output, which looks like it maybe reads pieces of memory which it shouldn't.

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

I've made some progress and it is almost working now, except when the combining / dead key is pressed with shift.

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

So, now when the input works, I am still fighting with output. I didn't put much time into it lately, but I am making slow progress.

I've added new primitive Xutf8DrawString_wrap using primitive maker, which uses Xutf8DrawString instead of XDrawString used so far. Problem is, that Xutf8DrawString is defined as:

void Xutf8DrawString(Display *display, Drawable d, XFontSet font_set, GC gc, int x, int y, char *string, int num_bytes); 

and XDrawString as

int XDrawString(Display *display, Drawable d, GC gc, int x, int y, char *string, int length); 

Notice, that Xutf8DrawString is using extra parameter XFontSet font_set. This means, that font is set differently and needs to be passed with each call, which means that I have to add XFontSet structure to Self using primitive maker and also change the Self code which handles setting of font.

from self.

davidungar avatar davidungar commented on July 27, 2024

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

unicode

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

It works as proof of concept as the hack on C++ level. It looks horribly, because of the different unicode fallback font, and aligns strings wrongly, because it still uses XFontStruct to compute width, instead of XFontSet. But it works.

Now I will have to port code to Self and also rewrite Xlib methods for querying the width of string and such to use XFontSet.

from self.

russellallen avatar russellallen commented on July 27, 2024

This is nice!
Are you still using the original bitmap X font system or the (less ancient) xft?

from self.

Bystroushaak avatar Bystroushaak commented on July 27, 2024

So far just the original bitmap system. I will look into xft.

from self.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.