X11: fix XIM input method support
This commit is contained in:
parent
e458f4651e
commit
2567551469
@ -40,6 +40,8 @@ namespace
|
||||
// The shared display and its reference counter
|
||||
Display* sharedDisplay = NULL;
|
||||
unsigned int referenceCount = 0;
|
||||
XIM sharedXIM = NULL;
|
||||
unsigned int referenceCountXIM = 0;
|
||||
sf::Mutex mutex;
|
||||
|
||||
typedef std::map<std::string, Atom> AtomMap;
|
||||
@ -85,6 +87,56 @@ void CloseDisplay(Display* display)
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
XIM OpenXIM()
|
||||
{
|
||||
Lock lock(mutex);
|
||||
|
||||
assert(sharedDisplay != NULL);
|
||||
|
||||
if (referenceCountXIM == 0)
|
||||
{
|
||||
// Create a new XIM instance
|
||||
|
||||
// We need the default (environment) locale and X locale for opening
|
||||
// the IM and properly receiving text
|
||||
// First save the previous ones (this might be able to be written more elegantly?)
|
||||
const char* p;
|
||||
std::string prevLoc((p = setlocale(LC_ALL, NULL)) ? p : "");
|
||||
std::string prevXLoc((p = XSetLocaleModifiers(NULL)) ? p : "");
|
||||
|
||||
// Set the locales from environment
|
||||
setlocale(LC_ALL, "");
|
||||
XSetLocaleModifiers("");
|
||||
|
||||
// Create the input context
|
||||
sharedXIM = XOpenIM(sharedDisplay, NULL, NULL, NULL);
|
||||
|
||||
// Restore the previous locale
|
||||
if (prevLoc.length() != 0)
|
||||
setlocale(LC_ALL, prevLoc.c_str());
|
||||
|
||||
if (prevXLoc.length() != 0)
|
||||
XSetLocaleModifiers(prevXLoc.c_str());
|
||||
}
|
||||
|
||||
referenceCountXIM++;
|
||||
|
||||
return sharedXIM;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
void CloseXIM(XIM xim)
|
||||
{
|
||||
Lock lock(mutex);
|
||||
|
||||
assert(xim == sharedXIM);
|
||||
|
||||
referenceCountXIM--;
|
||||
|
||||
if ((referenceCountXIM == 0) && (xim != NULL))
|
||||
XCloseIM(xim);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
Atom getAtom(const std::string& name, bool onlyIfExists)
|
||||
|
@ -55,6 +55,27 @@ Display* OpenDisplay();
|
||||
////////////////////////////////////////////////////////////
|
||||
void CloseDisplay(Display* display);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the shared XIM context for the Display
|
||||
///
|
||||
/// This function increments the reference count of the XIM context,
|
||||
/// it must be matched with a call to CloseXIM.
|
||||
///
|
||||
/// It must be called with a display already opened.
|
||||
///
|
||||
/// \return XIM handle (a pointer) of the context
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
XIM OpenXIM();
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Release a reference to the shared XIM context
|
||||
///
|
||||
/// \param xim XIM context to release
|
||||
///
|
||||
////////////////////////////////////////////////////////////
|
||||
void CloseXIM(XIM xim);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
/// \brief Get the atom with the specified name
|
||||
///
|
||||
|
@ -101,7 +101,9 @@ namespace
|
||||
Bool checkEvent(::Display*, XEvent* event, XPointer userData)
|
||||
{
|
||||
// Just check if the event matches the window
|
||||
return event->xany.window == reinterpret_cast< ::Window >(userData);
|
||||
// The input method sometimes sends ClientMessages with a different window ID,
|
||||
// our event loop has to process them for the IM to work
|
||||
return (event->xany.window == reinterpret_cast< ::Window >(userData)) || (event->type == ClientMessage);
|
||||
}
|
||||
|
||||
// Find the name of the current executable
|
||||
@ -803,7 +805,7 @@ WindowImplX11::~WindowImplX11()
|
||||
|
||||
// Close the input method
|
||||
if (m_inputMethod)
|
||||
XCloseIM(m_inputMethod);
|
||||
CloseXIM(m_inputMethod);
|
||||
|
||||
// Close the connection with the X server
|
||||
CloseDisplay(m_display);
|
||||
@ -1607,7 +1609,7 @@ void WindowImplX11::initialize()
|
||||
using namespace WindowsImplX11Impl;
|
||||
|
||||
// Create the input context
|
||||
m_inputMethod = XOpenIM(m_display, NULL, NULL, NULL);
|
||||
m_inputMethod = OpenXIM();
|
||||
|
||||
if (m_inputMethod)
|
||||
{
|
||||
@ -1845,27 +1847,31 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
||||
// Close event
|
||||
case ClientMessage:
|
||||
{
|
||||
static Atom wmProtocols = getAtom("WM_PROTOCOLS");
|
||||
|
||||
// Handle window manager protocol messages we support
|
||||
if (windowEvent.xclient.message_type == wmProtocols)
|
||||
// Input methods might want random ClientMessage events
|
||||
if (!XFilterEvent(&windowEvent, None))
|
||||
{
|
||||
static Atom wmDeleteWindow = getAtom("WM_DELETE_WINDOW");
|
||||
static Atom netWmPing = ewmhSupported() ? getAtom("_NET_WM_PING", true) : None;
|
||||
static Atom wmProtocols = getAtom("WM_PROTOCOLS");
|
||||
|
||||
if ((windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(wmDeleteWindow))
|
||||
// Handle window manager protocol messages we support
|
||||
if (windowEvent.xclient.message_type == wmProtocols)
|
||||
{
|
||||
// Handle the WM_DELETE_WINDOW message
|
||||
Event event;
|
||||
event.type = Event::Closed;
|
||||
pushEvent(event);
|
||||
}
|
||||
else if (netWmPing && (windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(netWmPing))
|
||||
{
|
||||
// Handle the _NET_WM_PING message, send pong back to WM to show that we are responsive
|
||||
windowEvent.xclient.window = DefaultRootWindow(m_display);
|
||||
static Atom wmDeleteWindow = getAtom("WM_DELETE_WINDOW");
|
||||
static Atom netWmPing = ewmhSupported() ? getAtom("_NET_WM_PING", true) : None;
|
||||
|
||||
XSendEvent(m_display, DefaultRootWindow(m_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &windowEvent);
|
||||
if ((windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(wmDeleteWindow))
|
||||
{
|
||||
// Handle the WM_DELETE_WINDOW message
|
||||
Event event;
|
||||
event.type = Event::Closed;
|
||||
pushEvent(event);
|
||||
}
|
||||
else if (netWmPing && (windowEvent.xclient.format == 32) && (windowEvent.xclient.data.l[0]) == static_cast<long>(netWmPing))
|
||||
{
|
||||
// Handle the _NET_WM_PING message, send pong back to WM to show that we are responsive
|
||||
windowEvent.xclient.window = DefaultRootWindow(m_display);
|
||||
|
||||
XSendEvent(m_display, DefaultRootWindow(m_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &windowEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1904,7 +1910,7 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
||||
if (m_inputContext)
|
||||
{
|
||||
Status status;
|
||||
Uint8 keyBuffer[16];
|
||||
Uint8 keyBuffer[64];
|
||||
|
||||
int length = Xutf8LookupString(
|
||||
m_inputContext,
|
||||
@ -1915,16 +1921,26 @@ bool WindowImplX11::processEvent(XEvent& windowEvent)
|
||||
&status
|
||||
);
|
||||
|
||||
if (length > 0)
|
||||
if (status == XBufferOverflow)
|
||||
err() << "A TextEntered event has more than 64 bytes of UTF-8 input, and "
|
||||
"has been discarded\nThis means either you have typed a very long string "
|
||||
"(more than 20 chars), or your input method is broken in obscure ways." << std::endl;
|
||||
else if (status == XLookupChars)
|
||||
{
|
||||
// There might be more than 1 characters in this event,
|
||||
// so we must iterate it
|
||||
Uint32 unicode = 0;
|
||||
Utf8::decode(keyBuffer, keyBuffer + length, unicode, 0);
|
||||
if (unicode != 0)
|
||||
Uint8* iter = keyBuffer;
|
||||
while (iter < keyBuffer + length)
|
||||
{
|
||||
Event textEvent;
|
||||
textEvent.type = Event::TextEntered;
|
||||
textEvent.text.unicode = unicode;
|
||||
pushEvent(textEvent);
|
||||
iter = Utf8::decode(iter, keyBuffer + length, unicode, 0);
|
||||
if (unicode != 0)
|
||||
{
|
||||
Event textEvent;
|
||||
textEvent.type = Event::TextEntered;
|
||||
textEvent.text.unicode = unicode;
|
||||
pushEvent(textEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user