Skip to content

Commit ca470cf

Browse files
committed
clipboard paste
1 parent 58477f3 commit ca470cf

File tree

4 files changed

+212
-108
lines changed

4 files changed

+212
-108
lines changed

Engine/system/api/x11api.cpp

Lines changed: 188 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ static Atom& _NET_WM_STATE_FULLSCREEN(){
7676
return w;
7777
}
7878

79+
static Atom& CLIPBOARD(){
80+
static Atom w = XInternAtom( dpy, "CLIPBOARD", 0);
81+
return w;
82+
}
83+
84+
static Atom& UTF8_STRING(){
85+
static Atom w = XInternAtom( dpy, "UTF8_STRING", 0);
86+
return w;
87+
}
88+
89+
static Atom& XSEL_DATA(){
90+
static Atom w = XInternAtom( dpy, "XSEL_DATA", 0);
91+
return w;
92+
}
93+
7994
static bool nativeIsFullscreen(SystemApi::Window *iw) {
8095
HWND w(iw);
8196

@@ -521,129 +536,167 @@ int X11Api::implExec(SystemApi::AppCallBack &cb) {
521536
return 0;
522537
}
523538

524-
void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) {
525-
// main message loop
526-
if(XPending(dpy)>0) {
527-
XEvent xev={};
528-
XNextEvent(dpy, &xev);
529-
530-
HWND hWnd = xev.xclient.window;
531-
auto it = impl->findWindow(hWnd.ptr());
532-
if(it==nullptr)
533-
return;
534-
Tempest::Window& cb = *it->owner;
535-
switch( xev.type ) {
536-
case ClientMessage: {
537-
if(xev.xclient.data.l[0] == long(WM_DELETE_WINDOW())){
538-
SystemApi::exit();
539-
}
540-
break;
541-
}
542-
case ConfigureNotify: {
543-
cb.setPosition(xev.xconfigure.x, xev.xconfigure.y);
544-
if(xev.xconfigure.width !=cb.w() || xev.xconfigure.height!=cb.h()) {
545-
Tempest::SizeEvent e(xev.xconfigure.width, xev.xconfigure.height);
546-
SystemApi::dispatchResize(cb,e);
547-
}
548-
break;
539+
void X11Api::dispatchEvent(XEvent &xev) {
540+
HWND hWnd = xev.xclient.window;
541+
auto it = impl->findWindow(hWnd.ptr());
542+
if(it==nullptr)
543+
return;
544+
Tempest::Window& cb = *it->owner;
545+
switch( xev.type ) {
546+
case ClientMessage: {
547+
if(xev.xclient.data.l[0] == long(WM_DELETE_WINDOW())){
548+
SystemApi::exit();
549549
}
550-
case PropertyNotify:{
551-
impl->trackFullscreen(hWnd.ptr());
552-
break;
550+
break;
551+
}
552+
case ConfigureNotify: {
553+
cb.setPosition(xev.xconfigure.x, xev.xconfigure.y);
554+
if(xev.xconfigure.width !=cb.w() || xev.xconfigure.height!=cb.h()) {
555+
Tempest::SizeEvent e(xev.xconfigure.width, xev.xconfigure.height);
556+
SystemApi::dispatchResize(cb,e);
553557
}
554-
case MappingNotify:
555-
XRefreshKeyboardMapping(&xev.xmapping);
556-
break;
557-
case ButtonPress:
558-
case ButtonRelease: {
559-
if(xev.xbutton.button==Button4 || xev.xbutton.button==Button5) {
560-
int ticks = 0;
561-
if( xev.xbutton.button == Button4 ) {
562-
ticks = 120;
563-
}
564-
else if ( xev.xbutton.button == Button5 ) {
565-
ticks = -120;
566-
}
567-
if(xev.type==ButtonPress) {
568-
Tempest::MouseEvent e( xev.xbutton.x,
569-
xev.xbutton.y,
570-
Tempest::Event::ButtonNone,
571-
Event::M_NoModifier,
572-
ticks,
573-
0,
574-
Event::MouseWheel );
575-
SystemApi::dispatchMouseWheel(cb, e);
576-
}
577-
} else {
578-
MouseEvent e( xev.xbutton.x,
579-
xev.xbutton.y,
580-
toButton( xev.xbutton ),
581-
Event::M_NoModifier,
582-
0,
583-
0,
584-
xev.type==ButtonPress ? Event::MouseDown : Event::MouseUp );
585-
if(xev.type==ButtonPress)
586-
SystemApi::dispatchMouseDown(cb, e); else
587-
SystemApi::dispatchMouseUp(cb, e);
558+
break;
559+
}
560+
case PropertyNotify:{
561+
impl->trackFullscreen(hWnd.ptr());
562+
break;
563+
}
564+
case MappingNotify:
565+
XRefreshKeyboardMapping(&xev.xmapping);
566+
break;
567+
case ButtonPress:
568+
case ButtonRelease: {
569+
if(xev.xbutton.button==Button4 || xev.xbutton.button==Button5) {
570+
int ticks = 0;
571+
if( xev.xbutton.button == Button4 ) {
572+
ticks = 120;
588573
}
589-
break;
590-
}
591-
case MotionNotify: {
592-
if(activeCursorChange == 1) {
593-
// FIXME: mouse behave crazy in OpenGothic
594-
activeCursorChange = 0;
595-
break;
574+
else if ( xev.xbutton.button == Button5 ) {
575+
ticks = -120;
576+
}
577+
if(xev.type==ButtonPress) {
578+
Tempest::MouseEvent e( xev.xbutton.x,
579+
xev.xbutton.y,
580+
Tempest::Event::ButtonNone,
581+
Event::M_NoModifier,
582+
ticks,
583+
0,
584+
Event::MouseWheel );
585+
SystemApi::dispatchMouseWheel(cb, e);
596586
}
597-
MouseEvent e( xev.xmotion.x,
598-
xev.xmotion.y,
599-
Event::ButtonNone,
587+
} else {
588+
MouseEvent e( xev.xbutton.x,
589+
xev.xbutton.y,
590+
toButton( xev.xbutton ),
600591
Event::M_NoModifier,
601592
0,
602593
0,
603-
Event::MouseMove );
604-
SystemApi::dispatchMouseMove(cb, e);
594+
xev.type==ButtonPress ? Event::MouseDown : Event::MouseUp );
595+
if(xev.type==ButtonPress)
596+
SystemApi::dispatchMouseDown(cb, e); else
597+
SystemApi::dispatchMouseUp(cb, e);
598+
}
599+
break;
600+
}
601+
case MotionNotify: {
602+
if(activeCursorChange == 1) {
603+
// FIXME: mouse behave crazy in OpenGothic
604+
activeCursorChange = 0;
605605
break;
606606
}
607-
case KeyPress:
608-
case KeyRelease: {
609-
KeySym ksym = XLookupKeysym(&xev.xkey,0);
610-
XIC xic = impl->xicOf(hWnd.ptr());
611-
612-
char txt[64]={};
613-
if(xic!=nullptr) {
614-
XEvent xev2 = xev;
615-
xev2.type = KeyPress; // HACK: Their behavior when a client passes a KeyRelease event is undefined.
616-
Status status = {};
617-
Xutf8LookupString(xic, &xev2.xkey, txt, sizeof(txt)-1, &ksym, &status);
618-
if(status == XBufferOverflow) {
619-
txt[0] = '\0';
620-
}
621-
// XLookupString(&xev.xkey, txt, sizeof(txt)-1, ksym, nullptr );
607+
MouseEvent e( xev.xmotion.x,
608+
xev.xmotion.y,
609+
Event::ButtonNone,
610+
Event::M_NoModifier,
611+
0,
612+
0,
613+
Event::MouseMove );
614+
SystemApi::dispatchMouseMove(cb, e);
615+
break;
616+
}
617+
case KeyPress:
618+
case KeyRelease: {
619+
KeySym ksym = XLookupKeysym(&xev.xkey,0);
620+
XIC xic = impl->xicOf(hWnd.ptr());
621+
622+
char txt[64]={};
623+
if(xic!=nullptr) {
624+
XEvent xev2 = xev;
625+
xev2.type = KeyPress; // HACK: Their behavior when a client passes a KeyRelease event is undefined.
626+
Status status = {};
627+
Xutf8LookupString(xic, &xev2.xkey, txt, sizeof(txt)-1, &ksym, &status);
628+
if(status == XBufferOverflow) {
629+
txt[0] = '\0';
622630
}
631+
// XLookupString(&xev.xkey, txt, sizeof(txt)-1, ksym, nullptr );
632+
}
623633

624-
auto u16 = TextCodec::toUtf16(txt); // TODO: remove dynamic allocation
625-
auto key = SystemApi::translateKey(ksym);
634+
auto u16 = TextCodec::toUtf16(txt); // TODO: remove dynamic allocation
635+
auto key = SystemApi::translateKey(ksym);
626636

627-
uint32_t scan = xev.xkey.keycode;
628-
// printf("%s\n",txt);
637+
uint32_t scan = xev.xkey.keycode;
638+
// printf("%s\n",txt);
629639

630-
Tempest::KeyEvent e(Event::KeyType(key),uint32_t(u16.size()>0 ? u16[0] : 0),Event::M_NoModifier,(xev.type==KeyPress) ? Event::KeyDown : Event::KeyUp);
631-
if(xev.type==KeyPress)
632-
SystemApi::dispatchKeyDown(cb,e,scan); else
633-
SystemApi::dispatchKeyUp (cb,e,scan);
634-
break;
635-
}
636-
case FocusIn: {
637-
FocusEvent e(true, Event::UnknownReason);
638-
SystemApi::dispatchFocus(cb, e);
639-
break;
640-
}
641-
case FocusOut: {
642-
FocusEvent e(false, Event::UnknownReason);
643-
SystemApi::dispatchFocus(cb, e);
644-
break;
640+
Atom actual_type;
641+
int actual_format;
642+
unsigned long nitems, bytes_after;
643+
unsigned char* data;
644+
645+
XGetWindowProperty(dpy, xev.xclient.window,
646+
XSEL_DATA(), 0L, ~0L, False, AnyPropertyType,
647+
&actual_type, &actual_format, &nitems, &bytes_after, &data);
648+
649+
Tempest::KeyEvent e(Event::KeyType(key),uint32_t(u16.size()>0 ? u16[0] : 0),Event::M_NoModifier,(xev.type==KeyPress) ? Event::KeyDown : Event::KeyUp);
650+
if(xev.type==KeyPress)
651+
SystemApi::dispatchKeyDown(cb,e,scan); else
652+
SystemApi::dispatchKeyUp (cb,e,scan);
653+
break;
654+
}
655+
case FocusIn: {
656+
FocusEvent e(true, Event::UnknownReason);
657+
SystemApi::dispatchFocus(cb, e);
658+
break;
659+
}
660+
case FocusOut: {
661+
FocusEvent e(false, Event::UnknownReason);
662+
SystemApi::dispatchFocus(cb, e);
663+
break;
664+
}
665+
case SelectionNotify: {
666+
clipboard.type = SystemApi::ClipboardDataType::Unknown;
667+
clipboard.data.clear();
668+
669+
if (xev.xselection.property != None) {
670+
Atom actual_type;
671+
int actual_format;
672+
unsigned long nitems, bytes_after;
673+
unsigned char* data;
674+
675+
XGetWindowProperty(xev.xselection.display, xev.xselection.requestor,
676+
xev.xselection.property, 0L, ~0L, False, AnyPropertyType,
677+
&actual_type, &actual_format, &nitems, &bytes_after, &data);
678+
679+
if (actual_type == UTF8_STRING() || actual_type == XA_STRING) {
680+
size_t total_bytes = nitems * (actual_format / 8);
681+
clipboard.type = SystemApi::ClipboardDataType::Text;
682+
clipboard.data.assign(data,data+total_bytes);
683+
XFree(data);
684+
}
685+
686+
XDeleteProperty(dpy, xev.xselection.requestor, xev.xselection.property);
645687
}
688+
break;
646689
}
690+
}
691+
}
692+
693+
void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) {
694+
// main message loop
695+
if(XPending(dpy)>0) {
696+
XEvent xev={};
697+
XNextEvent(dpy, &xev);
698+
699+
dispatchEvent(xev);
647700

648701
std::this_thread::yield();
649702
} else {
@@ -657,4 +710,31 @@ void X11Api::implProcessEvents(SystemApi::AppCallBack &cb) {
657710
}
658711
}
659712

713+
SystemApi::ClipboardData X11Api::implClipboardData(SystemApi::Window *w) {
714+
XConvertSelection(dpy, CLIPBOARD(), UTF8_STRING(), XSEL_DATA(), HWND(w), CurrentTime);
715+
716+
auto end = std::chrono::high_resolution_clock::now() + std::chrono::seconds(1);
717+
718+
// waiting for SelectionNotify
719+
while(true){
720+
if(XPending(dpy)>0) {
721+
XEvent xev={};
722+
XNextEvent(dpy, &xev);
723+
724+
dispatchEvent(xev);
725+
726+
if (xev.type == SelectionNotify) {
727+
break;
728+
}
729+
}
730+
731+
if(std::chrono::high_resolution_clock::now() > end) {
732+
Log::e("X11 clipboard paste timeout");
733+
break;
734+
}
735+
}
736+
737+
return clipboard;
738+
}
739+
660740
#endif

Engine/system/api/x11api.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include "system/systemapi.h"
44

5+
typedef union _XEvent XEvent;
6+
57
namespace Tempest {
68

79
class X11Api final: SystemApi {
@@ -34,9 +36,13 @@ class X11Api final: SystemApi {
3436
void implProcessEvents(AppCallBack& cb) override;
3537
bool implIsRunning() override;
3638

39+
void dispatchEvent(XEvent &xev);
3740
void alignGeometry(Window *w, Tempest::Window& owner);
3841

3942
std::unique_ptr<Private> impl;
43+
SystemApi::ClipboardData clipboard;
44+
45+
SystemApi::ClipboardData implClipboardData(SystemApi::Window *w) override;
4046

4147
friend class SystemApi;
4248
};

Engine/system/systemapi.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,7 @@ void SystemApi::showCursor(SystemApi::Window *w, CursorShape show) {
208208
float SystemApi::uiScale(Window* w) {
209209
return inst().implUiScale(w);
210210
}
211+
212+
SystemApi::ClipboardData SystemApi::clipboardData(SystemApi::Window *w) {
213+
return SystemApi::inst().implClipboardData(w);
214+
}

0 commit comments

Comments
 (0)