@@ -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+
7994static 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
0 commit comments