mirror of
https://github.com/AxioDL/boo.git
synced 2025-06-07 07:03:48 +00:00
Clipboard support for Xlib; initial IM support
This commit is contained in:
parent
1ed592e0a1
commit
aab693ff2f
@ -117,7 +117,7 @@ else(NOT GEKKO)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
|
include_directories(${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
|
||||||
list(APPEND _BOO_SYS_LIBS X11 Xi xkbcommon xkbcommon-x11 GL ${DBUS_LIBRARY} udev pthread)
|
list(APPEND _BOO_SYS_LIBS X11 Xi GL ${DBUS_LIBRARY} udev pthread)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define IWINDOW_HPP
|
#define IWINDOW_HPP
|
||||||
|
|
||||||
#include "System.hpp"
|
#include "System.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
@ -174,6 +175,14 @@ enum class EMouseCursor
|
|||||||
IBeam = 4
|
IBeam = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class EClipboardType
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
String = 1,
|
||||||
|
UTF8String = 2,
|
||||||
|
PNGImage = 3
|
||||||
|
};
|
||||||
|
|
||||||
class IWindow
|
class IWindow
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -211,6 +220,10 @@ public:
|
|||||||
virtual bool isFullscreen() const=0;
|
virtual bool isFullscreen() const=0;
|
||||||
virtual void setFullscreen(bool fs)=0;
|
virtual void setFullscreen(bool fs)=0;
|
||||||
|
|
||||||
|
virtual void claimKeyboardFocus()=0;
|
||||||
|
virtual bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)=0;
|
||||||
|
virtual std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)=0;
|
||||||
|
|
||||||
virtual void waitForRetrace()=0;
|
virtual void waitForRetrace()=0;
|
||||||
|
|
||||||
virtual uintptr_t getPlatformHandle() const=0;
|
virtual uintptr_t getPlatformHandle() const=0;
|
||||||
|
@ -35,6 +35,11 @@ static Window GetWindowOfEvent(XEvent* event, bool& windowEvent)
|
|||||||
{
|
{
|
||||||
switch (event->type)
|
switch (event->type)
|
||||||
{
|
{
|
||||||
|
case SelectionRequest:
|
||||||
|
{
|
||||||
|
windowEvent = true;
|
||||||
|
return event->xselectionrequest.owner;
|
||||||
|
}
|
||||||
case ClientMessage:
|
case ClientMessage:
|
||||||
{
|
{
|
||||||
windowEvent = true;
|
windowEvent = true;
|
||||||
@ -103,9 +108,45 @@ static Window GetWindowOfEvent(XEvent* event, bool& windowEvent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
IWindow* _WindowXlibNew(const std::string& title,
|
IWindow* _WindowXlibNew(const std::string& title,
|
||||||
Display* display, int defaultScreen,
|
Display* display, int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset,
|
||||||
GLXContext lastCtx);
|
GLXContext lastCtx);
|
||||||
|
|
||||||
|
static XIMStyle ChooseBetterStyle(XIMStyle style1, XIMStyle style2)
|
||||||
|
{
|
||||||
|
XIMStyle s,t;
|
||||||
|
XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks |
|
||||||
|
XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone;
|
||||||
|
XIMStyle status = XIMStatusArea | XIMStatusCallbacks |
|
||||||
|
XIMStatusNothing | XIMStatusNone;
|
||||||
|
if (style1 == 0) return style2;
|
||||||
|
if (style2 == 0) return style1;
|
||||||
|
if ((style1 & (preedit | status)) == (style2 & (preedit | status)))
|
||||||
|
return style1;
|
||||||
|
s = style1 & preedit;
|
||||||
|
t = style2 & preedit;
|
||||||
|
if (s != t) {
|
||||||
|
if (s | t | XIMPreeditCallbacks)
|
||||||
|
return (s == XIMPreeditCallbacks)?style1:style2;
|
||||||
|
else if (s | t | XIMPreeditPosition)
|
||||||
|
return (s == XIMPreeditPosition)?style1:style2;
|
||||||
|
else if (s | t | XIMPreeditArea)
|
||||||
|
return (s == XIMPreeditArea)?style1:style2;
|
||||||
|
else if (s | t | XIMPreeditNothing)
|
||||||
|
return (s == XIMPreeditNothing)?style1:style2;
|
||||||
|
}
|
||||||
|
else { /* if preedit flags are the same, compare status flags */
|
||||||
|
s = style1 & status;
|
||||||
|
t = style2 & status;
|
||||||
|
if (s | t | XIMStatusCallbacks)
|
||||||
|
return (s == XIMStatusCallbacks)?style1:style2;
|
||||||
|
else if (s | t | XIMStatusArea)
|
||||||
|
return (s == XIMStatusArea)?style1:style2;
|
||||||
|
else if (s | t | XIMStatusNothing)
|
||||||
|
return (s == XIMStatusNothing)?style1:style2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
class ApplicationXlib final : public IApplication
|
class ApplicationXlib final : public IApplication
|
||||||
{
|
{
|
||||||
IApplicationCallback& m_callback;
|
IApplicationCallback& m_callback;
|
||||||
@ -122,6 +163,9 @@ class ApplicationXlib final : public IApplication
|
|||||||
std::unordered_map<Window, IWindow*> m_windows;
|
std::unordered_map<Window, IWindow*> m_windows;
|
||||||
|
|
||||||
Display* m_xDisp = nullptr;
|
Display* m_xDisp = nullptr;
|
||||||
|
XIM m_xIM;
|
||||||
|
XFontSet m_fontset;
|
||||||
|
XIMStyle m_bestStyle = 0;
|
||||||
int m_xDefaultScreen = 0;
|
int m_xDefaultScreen = 0;
|
||||||
int m_xcbFd, m_dbusFd, m_maxFd;
|
int m_xcbFd, m_dbusFd, m_maxFd;
|
||||||
|
|
||||||
@ -193,6 +237,12 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setlocale(LC_ALL, "") == nullptr)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::FatalError, "Can't setlocale");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Open Xlib Display */
|
/* Open Xlib Display */
|
||||||
m_xDisp = XOpenDisplay(0);
|
m_xDisp = XOpenDisplay(0);
|
||||||
if (!m_xDisp)
|
if (!m_xDisp)
|
||||||
@ -201,6 +251,70 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Configure locale */
|
||||||
|
if (!XSupportsLocale()) {
|
||||||
|
Log.report(LogVisor::FatalError, "X does not support locale %s.",
|
||||||
|
setlocale(LC_ALL, nullptr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (XSetLocaleModifiers("") == nullptr)
|
||||||
|
Log.report(LogVisor::Warning, "Cannot set locale modifiers.");
|
||||||
|
|
||||||
|
if ((m_xIM = XOpenIM(m_xDisp, nullptr, nullptr, nullptr)) == nullptr)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::FatalError, "Couldn't open input method.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the fontset */
|
||||||
|
char** missing_charsets;
|
||||||
|
int num_missing_charsets = 0;
|
||||||
|
char* default_string;
|
||||||
|
m_fontset = XCreateFontSet(m_xDisp,
|
||||||
|
"-adobe-helvetica-*-r-*-*-*-120-*-*-*-*-*-*,\
|
||||||
|
-misc-fixed-*-r-*-*-*-130-*-*-*-*-*-*",
|
||||||
|
&missing_charsets, &num_missing_charsets,
|
||||||
|
&default_string);
|
||||||
|
/*
|
||||||
|
* if there are charsets for which no fonts can
|
||||||
|
* be found, print a warning message.
|
||||||
|
*/
|
||||||
|
if (num_missing_charsets > 0)
|
||||||
|
{
|
||||||
|
std::string warn("The following charsets are missing:\n");
|
||||||
|
|
||||||
|
for(int i=0 ; i<num_missing_charsets ; ++i)
|
||||||
|
warn += std::string(missing_charsets[i]) + '\n';
|
||||||
|
XFreeStringList(missing_charsets);
|
||||||
|
warn += "The string '" + std::string(default_string) + "' will be used in place.";
|
||||||
|
Log.report(LogVisor::Warning, warn.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* figure out which styles the IM can support */
|
||||||
|
XIMStyles* im_supported_styles;
|
||||||
|
XIMStyle app_supported_styles;
|
||||||
|
XGetIMValues(m_xIM, XNQueryInputStyle, &im_supported_styles, nullptr);
|
||||||
|
/* set flags for the styles our application can support */
|
||||||
|
app_supported_styles = XIMPreeditNone | XIMPreeditNothing | XIMPreeditArea;
|
||||||
|
app_supported_styles |= XIMStatusNone | XIMStatusNothing | XIMStatusArea;
|
||||||
|
/*
|
||||||
|
* now look at each of the IM supported styles, and
|
||||||
|
* chose the "best" one that we can support.
|
||||||
|
*/
|
||||||
|
for (int i=0 ; i<im_supported_styles->count_styles ; ++i)
|
||||||
|
{
|
||||||
|
XIMStyle style = im_supported_styles->supported_styles[i];
|
||||||
|
if ((style & app_supported_styles) == style) /* if we can handle it */
|
||||||
|
m_bestStyle = ChooseBetterStyle(style, m_bestStyle);
|
||||||
|
}
|
||||||
|
/* if we couldn't support any of them, print an error and exit */
|
||||||
|
if (m_bestStyle == 0)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::FatalError, "interaction style not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XFree(im_supported_styles);
|
||||||
|
|
||||||
m_xDefaultScreen = DefaultScreen(m_xDisp);
|
m_xDefaultScreen = DefaultScreen(m_xDisp);
|
||||||
X_CURSORS.m_pointer = XCreateFontCursor(m_xDisp, XC_left_ptr);
|
X_CURSORS.m_pointer = XCreateFontCursor(m_xDisp, XC_left_ptr);
|
||||||
X_CURSORS.m_hArrow = XCreateFontCursor(m_xDisp, XC_sb_h_double_arrow);
|
X_CURSORS.m_hArrow = XCreateFontCursor(m_xDisp, XC_sb_h_double_arrow);
|
||||||
@ -289,6 +403,7 @@ public:
|
|||||||
{
|
{
|
||||||
XEvent event;
|
XEvent event;
|
||||||
XNextEvent(m_xDisp, &event);
|
XNextEvent(m_xDisp, &event);
|
||||||
|
if (XFilterEvent(&event, None)) continue;
|
||||||
bool windowEvent;
|
bool windowEvent;
|
||||||
Window evWindow = GetWindowOfEvent(&event, windowEvent);
|
Window evWindow = GetWindowOfEvent(&event, windowEvent);
|
||||||
if (windowEvent)
|
if (windowEvent)
|
||||||
@ -355,7 +470,7 @@ public:
|
|||||||
|
|
||||||
IWindow* newWindow(const std::string& title)
|
IWindow* newWindow(const std::string& title)
|
||||||
{
|
{
|
||||||
IWindow* newWindow = _WindowXlibNew(title, m_xDisp, m_xDefaultScreen, m_lastGlxCtx);
|
IWindow* newWindow = _WindowXlibNew(title, m_xDisp, m_xDefaultScreen, m_xIM, m_bestStyle, m_fontset, m_lastGlxCtx);
|
||||||
m_windows[(Window)newWindow->getPlatformHandle()] = newWindow;
|
m_windows[(Window)newWindow->getPlatformHandle()] = newWindow;
|
||||||
return newWindow;
|
return newWindow;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,20 @@ struct WindowWayland : IWindow
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void claimKeyboardFocus()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<uint8_t[]>();
|
||||||
|
}
|
||||||
|
|
||||||
void waitForRetrace()
|
void waitForRetrace()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#define XK_XKB_KEYS
|
#define XK_XKB_KEYS
|
||||||
#define XK_LATIN1
|
#define XK_LATIN1
|
||||||
#include <X11/keysymdef.h>
|
#include <X11/keysymdef.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
#include <X11/extensions/XInput2.h>
|
#include <X11/extensions/XInput2.h>
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
@ -71,9 +70,10 @@ void GLXEnableVSync(Display* disp, GLXWindow drawable);
|
|||||||
|
|
||||||
extern int XINPUT_OPCODE;
|
extern int XINPUT_OPCODE;
|
||||||
|
|
||||||
static uint32_t translateKeysym(KeySym sym, ESpecialKey& specialSym, EModifierKey& modifierSym,
|
static uint32_t translateKeysym(XKeyEvent* ev, ESpecialKey& specialSym, EModifierKey& modifierSym, XIC xIC)
|
||||||
Display* d, unsigned state)
|
|
||||||
{
|
{
|
||||||
|
KeySym sym = XLookupKeysym(ev, 0);
|
||||||
|
|
||||||
specialSym = ESpecialKey::None;
|
specialSym = ESpecialKey::None;
|
||||||
modifierSym = EModifierKey::None;
|
modifierSym = EModifierKey::None;
|
||||||
if (sym >= XK_F1 && sym <= XK_F12)
|
if (sym >= XK_F1 && sym <= XK_F12)
|
||||||
@ -112,6 +112,7 @@ static uint32_t translateKeysym(KeySym sym, ESpecialKey& specialSym, EModifierKe
|
|||||||
modifierSym = EModifierKey::Alt;
|
modifierSym = EModifierKey::Alt;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
unsigned n;
|
unsigned n;
|
||||||
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
|
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
|
||||||
uint32_t utf = xkb_keysym_to_utf32(sym);
|
uint32_t utf = xkb_keysym_to_utf32(sym);
|
||||||
@ -119,6 +120,13 @@ static uint32_t translateKeysym(KeySym sym, ESpecialKey& specialSym, EModifierKe
|
|||||||
return toupper(utf);
|
return toupper(utf);
|
||||||
else
|
else
|
||||||
return utf;
|
return utf;
|
||||||
|
#endif
|
||||||
|
uint32_t utf = 0;
|
||||||
|
KeySym sym;
|
||||||
|
Status stat;
|
||||||
|
XmbLookupString(xIC, ev, (char*)&utf, 4, &sym, &stat);
|
||||||
|
if (stat == XLookupChars || stat == XLookupBoth)
|
||||||
|
return utf;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -154,7 +162,7 @@ static EMouseButton translateButton(unsigned detail)
|
|||||||
return EMouseButton::None;
|
return EMouseButton::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XCBAtoms
|
struct XlibAtoms
|
||||||
{
|
{
|
||||||
Atom m_wmProtocols = 0;
|
Atom m_wmProtocols = 0;
|
||||||
Atom m_wmDeleteWindow = 0;
|
Atom m_wmDeleteWindow = 0;
|
||||||
@ -163,7 +171,12 @@ struct XCBAtoms
|
|||||||
Atom m_netwmStateAdd = 0;
|
Atom m_netwmStateAdd = 0;
|
||||||
Atom m_netwmStateRemove = 0;
|
Atom m_netwmStateRemove = 0;
|
||||||
Atom m_motifWmHints = 0;
|
Atom m_motifWmHints = 0;
|
||||||
XCBAtoms(Display* disp)
|
Atom m_targets = 0;
|
||||||
|
Atom m_clipboard = 0;
|
||||||
|
Atom m_clipdata = 0;
|
||||||
|
Atom m_utf8String = 0;
|
||||||
|
Atom m_imagePng = 0;
|
||||||
|
XlibAtoms(Display* disp)
|
||||||
{
|
{
|
||||||
m_wmProtocols = XInternAtom(disp, "WM_PROTOCOLS", True);
|
m_wmProtocols = XInternAtom(disp, "WM_PROTOCOLS", True);
|
||||||
m_wmDeleteWindow = XInternAtom(disp, "WM_DELETE_WINDOW", True);
|
m_wmDeleteWindow = XInternAtom(disp, "WM_DELETE_WINDOW", True);
|
||||||
@ -172,9 +185,28 @@ struct XCBAtoms
|
|||||||
m_netwmStateAdd = XInternAtom(disp, "_NET_WM_STATE_ADD", False);
|
m_netwmStateAdd = XInternAtom(disp, "_NET_WM_STATE_ADD", False);
|
||||||
m_netwmStateRemove = XInternAtom(disp, "_NET_WM_STATE_REMOVE", False);
|
m_netwmStateRemove = XInternAtom(disp, "_NET_WM_STATE_REMOVE", False);
|
||||||
m_motifWmHints = XInternAtom(disp, "_MOTIF_WM_HINTS", True);
|
m_motifWmHints = XInternAtom(disp, "_MOTIF_WM_HINTS", True);
|
||||||
|
m_targets = XInternAtom(disp, "TARGETS", False);
|
||||||
|
m_clipboard = XInternAtom(disp, "CLIPBOARD", False);
|
||||||
|
m_clipdata = XInternAtom(disp, "CLIPDATA", False);
|
||||||
|
m_utf8String = XInternAtom(disp, "UTF8_STRING", False);
|
||||||
|
m_imagePng = XInternAtom(disp, "image/png", False);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
static XCBAtoms* S_ATOMS = NULL;
|
static XlibAtoms* S_ATOMS = NULL;
|
||||||
|
|
||||||
|
static Atom GetClipboardTypeAtom(EClipboardType t)
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case EClipboardType::String:
|
||||||
|
return XA_STRING;
|
||||||
|
case EClipboardType::UTF8String:
|
||||||
|
return S_ATOMS->m_utf8String;
|
||||||
|
case EClipboardType::PNGImage:
|
||||||
|
return S_ATOMS->m_imagePng;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void genFrameDefault(Screen* screen, int& xOut, int& yOut, int& wOut, int& hOut)
|
static void genFrameDefault(Screen* screen, int& xOut, int& yOut, int& wOut, int& hOut)
|
||||||
{
|
{
|
||||||
@ -477,6 +509,8 @@ class WindowXlib : public IWindow
|
|||||||
IWindowCallback* m_callback;
|
IWindowCallback* m_callback;
|
||||||
Colormap m_colormapId;
|
Colormap m_colormapId;
|
||||||
Window m_windowId;
|
Window m_windowId;
|
||||||
|
XIMStyle m_bestStyle;
|
||||||
|
XIC m_xIC;
|
||||||
GraphicsContextGLX m_gfxCtx;
|
GraphicsContextGLX m_gfxCtx;
|
||||||
uint32_t m_visualId;
|
uint32_t m_visualId;
|
||||||
|
|
||||||
@ -519,17 +553,17 @@ class WindowXlib : public IWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
WindowXlib(const std::string& title,
|
WindowXlib(const std::string& title,
|
||||||
Display* display, int defaultScreen,
|
Display* display, int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset,
|
||||||
GLXContext lastCtx)
|
GLXContext lastCtx)
|
||||||
: m_xDisp(display), m_callback(nullptr),
|
: m_xDisp(display), m_callback(nullptr),
|
||||||
m_gfxCtx(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
|
m_gfxCtx(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
|
||||||
this, display, defaultScreen,
|
this, display, defaultScreen,
|
||||||
lastCtx, m_visualId)
|
lastCtx, m_visualId),
|
||||||
|
m_bestStyle(bestInputStyle)
|
||||||
{
|
{
|
||||||
if (!S_ATOMS)
|
if (!S_ATOMS)
|
||||||
S_ATOMS = new XCBAtoms(display);
|
S_ATOMS = new XlibAtoms(display);
|
||||||
|
|
||||||
/* Default screen */
|
/* Default screen */
|
||||||
Screen* screen = ScreenOfDisplay(display, defaultScreen);
|
Screen* screen = ScreenOfDisplay(display, defaultScreen);
|
||||||
@ -565,6 +599,26 @@ public:
|
|||||||
CopyFromParent, CopyFromParent, selectedVisual,
|
CopyFromParent, CopyFromParent, selectedVisual,
|
||||||
CWBorderPixel | CWEventMask | CWColormap, &swa);
|
CWBorderPixel | CWEventMask | CWColormap, &swa);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now go create an IC using the style we chose.
|
||||||
|
* Also set the window and fontset attributes now.
|
||||||
|
*/
|
||||||
|
XVaNestedList list = XVaCreateNestedList(0, XNFontSet, fontset, nullptr);
|
||||||
|
m_xIC = XCreateIC(xIM, XNInputStyle, bestInputStyle,
|
||||||
|
XNClientWindow, m_windowId,
|
||||||
|
XNPreeditAttributes, list,
|
||||||
|
XNStatusAttributes, list,
|
||||||
|
nullptr);
|
||||||
|
XFree(list);
|
||||||
|
if (m_xIC == nullptr)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::FatalError, "Couldn't create input context.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long im_event_mask;
|
||||||
|
XGetICValues(m_xIC, XNFilterEvents, &im_event_mask, nullptr);
|
||||||
|
XSelectInput(display, m_windowId, swa.event_mask | im_event_mask);
|
||||||
|
XSetICFocus(m_xIC);
|
||||||
|
|
||||||
/* The XInput 2.1 extension enables per-pixel smooth scrolling trackpads */
|
/* The XInput 2.1 extension enables per-pixel smooth scrolling trackpads */
|
||||||
XIEventMask mask = {XIAllMasterDevices, XIMaskLen(XI_LASTEVENT)};
|
XIEventMask mask = {XIAllMasterDevices, XIMaskLen(XI_LASTEVENT)};
|
||||||
@ -832,6 +886,186 @@ public:
|
|||||||
m_inFs = fs;
|
m_inFs = fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ClipData
|
||||||
|
{
|
||||||
|
EClipboardType m_type = EClipboardType::None;
|
||||||
|
std::unique_ptr<uint8_t[]> m_data;
|
||||||
|
size_t m_sz = 0;
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_type = EClipboardType::None;
|
||||||
|
m_data.reset();
|
||||||
|
m_sz = 0;
|
||||||
|
}
|
||||||
|
} m_clipData;
|
||||||
|
|
||||||
|
void claimKeyboardFocus()
|
||||||
|
{
|
||||||
|
XLockDisplay(m_xDisp);
|
||||||
|
XSetICFocus(m_xIC);
|
||||||
|
XUnlockDisplay(m_xDisp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)
|
||||||
|
{
|
||||||
|
Atom xType = GetClipboardTypeAtom(type);
|
||||||
|
if (!xType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
XLockDisplay(m_xDisp);
|
||||||
|
m_clipData.m_type = type;
|
||||||
|
m_clipData.m_data.reset(new uint8_t[sz]);
|
||||||
|
m_clipData.m_sz = sz;
|
||||||
|
memcpy(m_clipData.m_data.get(), data, sz);
|
||||||
|
XSetSelectionOwner(m_xDisp, S_ATOMS->m_clipboard, m_windowId, CurrentTime);
|
||||||
|
XUnlockDisplay(m_xDisp);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)
|
||||||
|
{
|
||||||
|
Atom xType = GetClipboardTypeAtom(type);
|
||||||
|
if (!xType)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
XLockDisplay(m_xDisp);
|
||||||
|
XConvertSelection(m_xDisp, S_ATOMS->m_clipboard, xType, S_ATOMS->m_clipdata, m_windowId, CurrentTime);
|
||||||
|
XFlush(m_xDisp);
|
||||||
|
XEvent event;
|
||||||
|
for (int i=0 ; i<20; ++i)
|
||||||
|
{
|
||||||
|
if (XCheckTypedWindowEvent(m_xDisp, m_windowId, SelectionNotify, &event))
|
||||||
|
{
|
||||||
|
if (event.xselection.property != 0)
|
||||||
|
{
|
||||||
|
XSync(m_xDisp, false);
|
||||||
|
|
||||||
|
unsigned long nitems, rem;
|
||||||
|
int format;
|
||||||
|
unsigned char* data;
|
||||||
|
Atom type;
|
||||||
|
|
||||||
|
Atom t1 = S_ATOMS->m_clipboard;
|
||||||
|
Atom t2 = S_ATOMS->m_clipdata;
|
||||||
|
|
||||||
|
if (XGetWindowProperty(m_xDisp, m_windowId, S_ATOMS->m_clipdata, 0, 32, False, AnyPropertyType,
|
||||||
|
&type, &format, &nitems, &rem, &data))
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::FatalError, "Clipboard allocation failed");
|
||||||
|
XUnlockDisplay(m_xDisp);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rem != 0)
|
||||||
|
{
|
||||||
|
Log.report(LogVisor::FatalError, "partial clipboard read");
|
||||||
|
XUnlockDisplay(m_xDisp);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
sz = nitems * format / 8;
|
||||||
|
std::unique_ptr<uint8_t[]> ret(new uint8_t[sz]);
|
||||||
|
memcpy(ret.get(), data, sz);
|
||||||
|
XFree(data);
|
||||||
|
XUnlockDisplay(m_xDisp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
XUnlockDisplay(m_xDisp);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (XCheckTypedWindowEvent(m_xDisp, m_windowId, SelectionRequest, &event) &&
|
||||||
|
event.xselectionrequest.owner == m_windowId)
|
||||||
|
handleSelectionRequest(&event.xselectionrequest);
|
||||||
|
if (XCheckTypedWindowEvent(m_xDisp, m_windowId, SelectionClear, &event) &&
|
||||||
|
event.xselectionclear.window == m_windowId)
|
||||||
|
m_clipData.clear();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
}
|
||||||
|
XUnlockDisplay(m_xDisp);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleSelectionRequest(XSelectionRequestEvent* se)
|
||||||
|
{
|
||||||
|
XEvent reply;
|
||||||
|
reply.xselection.type = SelectionNotify;
|
||||||
|
reply.xselection.display = m_xDisp;
|
||||||
|
reply.xselection.requestor = se->requestor;
|
||||||
|
reply.xselection.selection = se->selection;
|
||||||
|
reply.xselection.target = se->target;
|
||||||
|
reply.xselection.time = se->time;
|
||||||
|
reply.xselection.property = se->property;
|
||||||
|
if (se->target == S_ATOMS->m_targets)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (se->selection == XA_PRIMARY)
|
||||||
|
{
|
||||||
|
std::vector<Atom> x(sel_formats.GetCount());
|
||||||
|
for (int i=0 ; i<sel_formats.GetCount() ; ++i)
|
||||||
|
{
|
||||||
|
x[i] = XAtom(sel_formats[i]);
|
||||||
|
}
|
||||||
|
XChangeProperty(m_xDisp, se->requestor, se->property, XA_ATOM,
|
||||||
|
32, 0, (unsigned char*)x.data(),
|
||||||
|
sel_formats.GetCount());
|
||||||
|
}
|
||||||
|
else if (se->selection == S_ATOMS->m_clipboard)
|
||||||
|
{
|
||||||
|
std::vector<Atom> x(data.GetCount());
|
||||||
|
for (int i=0 ; i<data.GetCount() ; ++i)
|
||||||
|
{
|
||||||
|
x[i] = data.GetKey(i);
|
||||||
|
}
|
||||||
|
XChangeProperty(m_xDisp, se->requestor, se->property, XA_ATOM,
|
||||||
|
32, 0, (unsigned char*)x.data(),
|
||||||
|
data.GetCount());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Atom ValidTargets[] = {GetClipboardTypeAtom(m_clipData.m_type)};
|
||||||
|
XChangeProperty(m_xDisp, se->requestor, se->property, XA_ATOM,
|
||||||
|
32, 0, (unsigned char*)ValidTargets, m_clipData.m_type != EClipboardType::None);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (se->selection == XA_PRIMARY)
|
||||||
|
{
|
||||||
|
String fmt = XAtomName(se->target);
|
||||||
|
int i = sel_formats.Find(fmt);
|
||||||
|
if (i >= 0 && sel_ctrl)
|
||||||
|
{
|
||||||
|
String d = sel_ctrl->GetSelectionData(fmt);
|
||||||
|
XChangeProperty(m_xDisp, se->requestor, se->property, se->target, 8, PropModeReplace,
|
||||||
|
d, d.GetLength());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
reply.xselection.property = 0;
|
||||||
|
}
|
||||||
|
else if (se->selection == S_ATOMS->m_clipboard)
|
||||||
|
{
|
||||||
|
int i = data.Find(se->target);
|
||||||
|
if (i >= 0)
|
||||||
|
{
|
||||||
|
String d = data[i].Render();
|
||||||
|
XChangeProperty(m_xDisp, se->requestor, se->property, se->target, 8, PropModeReplace,
|
||||||
|
d, d.GetLength());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
reply.xselection.property = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (se->target == GetClipboardTypeAtom(m_clipData.m_type))
|
||||||
|
{
|
||||||
|
XChangeProperty(m_xDisp, se->requestor, se->property, se->target, 8, PropModeReplace,
|
||||||
|
m_clipData.m_data.get(), m_clipData.m_sz);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
reply.xselection.property = 0;
|
||||||
|
}
|
||||||
|
XSendEvent(m_xDisp, se->requestor, False, 0, &reply);
|
||||||
|
}
|
||||||
|
|
||||||
void waitForRetrace()
|
void waitForRetrace()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(m_gfxCtx.m_vsyncmt);
|
std::unique_lock<std::mutex> lk(m_gfxCtx.m_vsyncmt);
|
||||||
@ -943,11 +1177,43 @@ public:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This procedure sets the application's size constraints and returns
|
||||||
|
* the IM's preferred size for either the Preedit or Status areas,
|
||||||
|
* depending on the value of the name argument. The area argument is
|
||||||
|
* used to pass the constraints and to return the preferred size.
|
||||||
|
*/
|
||||||
|
void GetPreferredGeometry(const char* name, XRectangle* area)
|
||||||
|
{
|
||||||
|
XVaNestedList list;
|
||||||
|
list = XVaCreateNestedList(0, XNAreaNeeded, area, nullptr);
|
||||||
|
/* set the constraints */
|
||||||
|
XSetICValues(m_xIC, name, list, nullptr);
|
||||||
|
/* query the preferred size */
|
||||||
|
XGetICValues(m_xIC, name, list, nullptr);
|
||||||
|
XFree(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This procedure sets the geometry of either the Preedit or Status
|
||||||
|
* Areas, depending on the value of the name argument.
|
||||||
|
*/
|
||||||
|
void SetGeometry(const char* name, XRectangle* area)
|
||||||
|
{
|
||||||
|
XVaNestedList list;
|
||||||
|
list = XVaCreateNestedList(0, XNArea, area, nullptr);
|
||||||
|
XSetICValues(m_xIC, name, list, nullptr);
|
||||||
|
XFree(list);
|
||||||
|
}
|
||||||
|
|
||||||
void _incomingEvent(void* e)
|
void _incomingEvent(void* e)
|
||||||
{
|
{
|
||||||
XEvent* event = (XEvent*)e;
|
XEvent* event = (XEvent*)e;
|
||||||
switch (event->type)
|
switch (event->type)
|
||||||
{
|
{
|
||||||
|
case SelectionRequest:
|
||||||
|
{
|
||||||
|
handleSelectionRequest(&event->xselectionrequest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case ClientMessage:
|
case ClientMessage:
|
||||||
{
|
{
|
||||||
if (event->xclient.data.l[0] == S_ATOMS->m_wmDeleteWindow && m_callback)
|
if (event->xclient.data.l[0] == S_ATOMS->m_wmDeleteWindow && m_callback)
|
||||||
@ -990,6 +1256,27 @@ public:
|
|||||||
m_ww = event->xconfigure.width;
|
m_ww = event->xconfigure.width;
|
||||||
m_wh = event->xconfigure.height;
|
m_wh = event->xconfigure.height;
|
||||||
|
|
||||||
|
if (m_bestStyle & XIMPreeditArea)
|
||||||
|
{
|
||||||
|
XRectangle preedit_area;
|
||||||
|
preedit_area.width = event->xconfigure.width*4/5;
|
||||||
|
preedit_area.height = 0;
|
||||||
|
GetPreferredGeometry(XNPreeditAttributes, &preedit_area);
|
||||||
|
preedit_area.x = event->xconfigure.width - preedit_area.width;
|
||||||
|
preedit_area.y = event->xconfigure.height - preedit_area.height;
|
||||||
|
SetGeometry(XNPreeditAttributes, &preedit_area);
|
||||||
|
}
|
||||||
|
if (m_bestStyle & XIMStatusArea)
|
||||||
|
{
|
||||||
|
XRectangle status_area;
|
||||||
|
status_area.width = event->xconfigure.width/5;
|
||||||
|
status_area.height = 0;
|
||||||
|
GetPreferredGeometry(XNStatusAttributes, &status_area);
|
||||||
|
status_area.x = 0;
|
||||||
|
status_area.y = event->xconfigure.height - status_area.height;
|
||||||
|
SetGeometry(XNStatusAttributes, &status_area);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_callback)
|
if (m_callback)
|
||||||
{
|
{
|
||||||
SWindowRect rect =
|
SWindowRect rect =
|
||||||
@ -1004,9 +1291,10 @@ public:
|
|||||||
{
|
{
|
||||||
ESpecialKey specialKey;
|
ESpecialKey specialKey;
|
||||||
EModifierKey modifierKey;
|
EModifierKey modifierKey;
|
||||||
uint32_t charCode = translateKeysym(XLookupKeysym(&event->xkey, 0),
|
unsigned int state = event->xkey.state;
|
||||||
specialKey, modifierKey, m_xDisp, event->xkey.state);
|
event->xkey.state &= ~ControlMask;
|
||||||
EModifierKey modifierMask = translateModifiers(event->xkey.state);
|
uint32_t charCode = translateKeysym(&event->xkey, specialKey, modifierKey, m_xIC);
|
||||||
|
EModifierKey modifierMask = translateModifiers(state);
|
||||||
if (charCode)
|
if (charCode)
|
||||||
m_callback->charKeyDown(charCode, modifierMask, false);
|
m_callback->charKeyDown(charCode, modifierMask, false);
|
||||||
else if (specialKey != ESpecialKey::None)
|
else if (specialKey != ESpecialKey::None)
|
||||||
@ -1022,9 +1310,10 @@ public:
|
|||||||
{
|
{
|
||||||
ESpecialKey specialKey;
|
ESpecialKey specialKey;
|
||||||
EModifierKey modifierKey;
|
EModifierKey modifierKey;
|
||||||
uint32_t charCode = translateKeysym(XLookupKeysym(&event->xkey, 0),
|
unsigned int state = event->xkey.state;
|
||||||
specialKey, modifierKey, m_xDisp, event->xkey.state);
|
event->xkey.state &= ~ControlMask;
|
||||||
EModifierKey modifierMask = translateModifiers(event->xkey.state);
|
uint32_t charCode = translateKeysym(&event->xkey, specialKey, modifierKey, m_xIC);
|
||||||
|
EModifierKey modifierMask = translateModifiers(state);
|
||||||
if (charCode)
|
if (charCode)
|
||||||
m_callback->charKeyUp(charCode, modifierMask);
|
m_callback->charKeyUp(charCode, modifierMask);
|
||||||
else if (specialKey != ESpecialKey::None)
|
else if (specialKey != ESpecialKey::None)
|
||||||
@ -1302,11 +1591,11 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
IWindow* _WindowXlibNew(const std::string& title,
|
IWindow* _WindowXlibNew(const std::string& title,
|
||||||
Display* display, int defaultScreen,
|
Display* display, int defaultScreen, XIM xIM, XIMStyle bestInputStyle, XFontSet fontset,
|
||||||
GLXContext lastCtx)
|
GLXContext lastCtx)
|
||||||
{
|
{
|
||||||
XLockDisplay(display);
|
XLockDisplay(display);
|
||||||
IWindow* ret = new WindowXlib(title, display, defaultScreen, lastCtx);
|
IWindow* ret = new WindowXlib(title, display, defaultScreen, xIM, bestInputStyle, fontset, lastCtx);
|
||||||
XUnlockDisplay(display);
|
XUnlockDisplay(display);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user