ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/clip_unix.cpp
(Generate patch)

Comparing SheepShaver/src/Unix/clip_unix.cpp (file contents):
Revision 1.2 by gbeauche, 2003-12-31T11:37:26Z vs.
Revision 1.3 by gbeauche, 2003-12-31T18:16:55Z

# Line 18 | Line 18
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  
21 + /*
22 + *  NOTES:
23 + *
24 + *  We must have (fast) X11 display locking routines. Otherwise, we
25 + *  can corrupt the X11 event queue from the emulator thread whereas
26 + *  the redraw thread is expected to handle them.
27 + *
28 + *  Two functions are exported to video_x.cpp:
29 + *    - ClipboardSelectionClear()
30 + *      called when we lose the selection ownership
31 + *    - ClipboardSelectionRequest()
32 + *      called when another client wants our clipboard data
33 + *  The display is locked by the redraw thread during their execution.
34 + *
35 + *  On PutScrap (Mac application wrote to clipboard), we always cache
36 + *  the Mac clipboard to a local structure (clip_data). Then, the
37 + *  selection ownership is grabbed until ClipboardSelectionClear()
38 + *  occurs. In that case, contents in cache becomes invalid.
39 + *
40 + *  On GetScrap (Mac application reads clipboard), we always fetch
41 + *  data from the X11 clipboard and immediately put it back to Mac
42 + *  side. Local cache does not need to be updated. If the selection
43 + *  owner supports the TIMESTAMP target, we can avoid useless copies.
44 + *
45 + *  For safety purposes, we lock the X11 display in the emulator
46 + *  thread during the whole GetScrap/PutScrap execution. Of course, we
47 + *  temporarily release the lock when waiting for SelectioNotify.
48 + */
49 +
50   #include "sysdeps.h"
51  
52   #include <X11/Xlib.h>
53   #include <X11/Xatom.h>
54   #include <pthread.h>
26 #include <set>
55   #include <vector>
56  
57   #include "macos_util.h"
# Line 37 | Line 65
65   #include "debug.h"
66  
67   #ifndef NO_STD_NAMESPACE
40 using std::set;
68   using std::vector;
69   #endif
70  
# Line 122 | Line 149 | struct ByteArray : public vector<uint8>
149          uint8 *data() { return &at(0); }
150   };
151  
152 < // Clipboard locks
126 < #ifdef HAVE_PTHREADS
127 < static pthread_mutex_t clip_lock = PTHREAD_MUTEX_INITIALIZER;
128 < #define LOCK_CLIPBOARD pthread_mutex_lock(&clip_lock);
129 < #define UNLOCK_CLIPBOARD pthread_mutex_unlock(&clip_lock);
130 < #elif defined(HAVE_SPINLOCKS)
131 < static spinlock_t clip_lock = SPIN_LOCK_UNLOCKED;
132 < #define LOCK_CLIPBOARD spin_lock(&clip_lock)
133 < #define UNLOCK_CLIPBOARD spin_unlock(&clip_lock)
134 < #else
135 < #define LOCK_CLIPBOARD
136 < #define UNLOCK_CLIPBOARD
137 < #endif
138 <
139 < // Clipboard data
152 > // Clipboard data for requestors
153   struct ClipboardData {
154          Time time;
155 <        uint32 type;
155 >        Atom type;
156          ByteArray data;
157   };
158   static ClipboardData clip_data;
159  
160 + // Prototypes
161 + static void do_putscrap(uint32 type, void *scrap, int32 length);
162 + static void do_getscrap(void **handle, uint32 type, int32 offset);
163 +
164  
165   /*
166   *  Read an X11 property (hack from QT 3.1.2)
# Line 231 | Line 248 | static bool read_property(Display *dpy,
248   */
249  
250   static const uint64 SELECTION_MAX_WAIT = 500000; // 500 ms
234 static volatile bool xselection_event_avail;
235 static XSelectionEvent xselection_event;
251  
252 < static bool wait_for_selection_notify_event(Display *dpy, Window win, int timeout)
252 > static bool wait_for_selection_notify_event(Display *dpy, Window win, XEvent *event, int timeout)
253   {
254          uint64 start = GetTicks_usec();
255  
241        LOCK_CLIPBOARD;
242        xselection_event_avail = false;
243        UNLOCK_CLIPBOARD;
244
245        // First wait a very short period of time < the VideoRefresh() resolution
246        struct timespec req = {0, 5000000};
247        nanosleep(&req, NULL);
248
249        // FIXME: Since handle_events() is in a separate thread, wait for
250        // a SelectionNotify event to occur and reported here. The
251        // resolution of this wait action should match that of VideoRefresh()
252        if (xselection_event_avail)
253                return true;
254
256          do {
257                  // Wait
258 <                struct timespec req = {0, 16666667};
259 <                nanosleep(&req, NULL);
258 >                XDisplayUnlock();
259 >                Delay_usec(5000);
260 >                XDisplayLock();
261  
262 <                // Return immediately if the event is now available
263 <                if (xselection_event_avail)
262 >                // Check for SelectionNotify event
263 >                if (XCheckTypedWindowEvent(dpy, win, SelectionNotify, event))
264                          return true;
265  
266          } while ((GetTicks_usec() - start) < timeout);
# Line 352 | Line 354 | void PutScrap(uint32 type, void *scrap,
354          if (length <= 0)
355                  return;
356  
357 <        bool did_putscrap = false;
357 >        XDisplayLock();
358 >        do_putscrap(type, scrap, length);
359 >        XDisplayUnlock();
360 > }
361 >
362 > static void do_putscrap(uint32 type, void *scrap, int32 length)
363 > {
364 >        clip_data.type = None;
365          switch (type) {
366 <                case FOURCC('T','E','X','T'):
367 <                        D(bug(" clipping TEXT\n"));
368 <                        clip_data.type = type;
369 <                        clip_data.data.clear();
370 <                        clip_data.data.reserve(length);
371 <
372 <                        // Convert text from Mac charset to ISO-Latin1
373 <                        uint8 *p = (uint8 *)scrap;
374 <                        for (int i=0; i<length; i++) {
375 <                                uint8 c = *p++;
376 <                                if (c < 0x80) {
377 <                                        if (c == 13)    // CR -> LF
378 <                                                c = 10;
379 <                                } else if (!no_clip_conversion)
380 <                                        c = mac2iso[c & 0x7f];
381 <                                clip_data.data.push_back(c);
382 <                        }
383 <                        did_putscrap = true;
375 <                        break;
366 >        case FOURCC('T','E','X','T'):
367 >                D(bug(" clipping TEXT\n"));
368 >                clip_data.type = XA_STRING;
369 >                clip_data.data.clear();
370 >                clip_data.data.reserve(length);
371 >
372 >                // Convert text from Mac charset to ISO-Latin1
373 >                uint8 *p = (uint8 *)scrap;
374 >                for (int i=0; i<length; i++) {
375 >                        uint8 c = *p++;
376 >                        if (c < 0x80) {
377 >                                if (c == 13)    // CR -> LF
378 >                                        c = 10;
379 >                        } else if (!no_clip_conversion)
380 >                                c = mac2iso[c & 0x7f];
381 >                        clip_data.data.push_back(c);
382 >                }
383 >                break;
384          }
385  
386          // Acquire selection ownership
387 <        if (did_putscrap) {
387 >        if (clip_data.type != None) {
388                  clip_data.time = CurrentTime;
389                  while (XGetSelectionOwner(x_display, xa_clipboard) != clip_win)
390                          XSetSelectionOwner(x_display, xa_clipboard, clip_win, clip_data.time);
# Line 394 | Line 402 | void GetScrap(void **handle, uint32 type
402          if (!REPLACE_GETSCRAP)
403                  return;
404  
405 +        XDisplayLock();
406 +        do_getscrap(handle, type, offset);
407 +        XDisplayUnlock();
408 + }
409 +
410 + static void do_getscrap(void **handle, uint32 type, int32 offset)
411 + {
412 +        ByteArray data;
413 +        XEvent event;
414 +
415 +        // If we own the selection, the data is already available on MacOS side
416 +        if (XGetSelectionOwner(x_display, xa_clipboard) == clip_win)
417 +                return;
418 +
419          // Check TIMESTAMP
420   #if GETSCRAP_REQUESTS_TIMESTAMP
421          static Time last_timestamp = 0;
422          XConvertSelection(x_display, xa_clipboard, xa_timestamp, xa_clipboard, clip_win, CurrentTime);
423 <        if (wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) &&
424 <                xselection_event.property != None) {
425 <                ByteArray data;
426 <                if (read_property(x_display,
427 <                                                  xselection_event.requestor, xselection_event.property,
428 <                                                  true, data, 0, 0, 0, false)) {
429 <                        Time timestamp = ((long *)data.data())[0];
430 <                        if (timestamp == last_timestamp)
409 <                                return;
410 <                }
423 >        if (wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) &&
424 >                event.xselection.property != None &&
425 >                read_property(x_display,
426 >                                          event.xselection.requestor, event.xselection.property,
427 >                                          true, data, 0, 0, 0, false)) {
428 >                Time timestamp = ((long *)data.data())[0];
429 >                if (timestamp <= last_timestamp)
430 >                        return;
431          }
432 +        last_timestamp = CurrentTime;
433   #endif
434  
435          // Get TARGETS available
436   #if GETSCRAP_REQUESTS_TARGETS
437          XConvertSelection(x_display, xa_clipboard, xa_targets, xa_clipboard, clip_win, CurrentTime);
438 <        if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
439 <                xselection_event.property == None)
440 <                return;
441 <
421 <        ByteArray data;
422 <        if (!read_property(x_display,
423 <                                           xselection_event.requestor, xselection_event.property,
438 >        if (!wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) ||
439 >                event.xselection.property == None ||
440 >                !read_property(x_display,
441 >                                           event.xselection.requestor, event.xselection.property,
442                                             true, data, 0, 0, 0, false))
443                  return;
444   #endif
# Line 457 | Line 475 | void GetScrap(void **handle, uint32 type
475  
476          // Get the native clipboard data
477          XConvertSelection(x_display, xa_clipboard, format, xa_clipboard, clip_win, CurrentTime);
478 <        if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
479 <                xselection_event.property == None ||
478 >        if (!wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) ||
479 >                event.xselection.property == None ||
480                  !read_property(x_display,
481 <                                           xselection_event.requestor, xselection_event.property,
482 <                                           false, clip_data.data, 0, 0, 0, format == XA_STRING))
481 >                                           event.xselection.requestor, event.xselection.property,
482 >                                           false, data, 0, 0, 0, format == XA_STRING))
483                  return;
484  
467        clip_data.type = type;
468        clip_data.time = xselection_event.time;
469
485          // Allocate space for new scrap in MacOS side
486          M68kRegisters r;
487 <        r.d[0] = clip_data.data.size();
487 >        r.d[0] = data.size();
488          Execute68kTrap(0xa71e, &r);                     // NewPtrSysClear()
489          uint32 scrap_area = r.a[0];
490  
# Line 478 | Line 493 | void GetScrap(void **handle, uint32 type
493                  case FOURCC('T','E','X','T'):
494                          // Convert text from ISO-Latin1 to Mac charset
495                          uint8 *p = Mac2HostAddr(scrap_area);
496 <                        for (int i = 0; i < clip_data.data.size(); i++) {
497 <                                uint8 c = clip_data.data[i];
496 >                        for (int i = 0; i < data.size(); i++) {
497 >                                uint8 c = data[i];
498                                  if (c < 0x80) {
499                                          if (c == 10)    // LF -> CR
500                                                  c = 13;
# Line 502 | Line 517 | void GetScrap(void **handle, uint32 type
517                          M68K_RTS >> 8, M68K_RTS
518                  };
519                  uint32 proc_area = (uint32)proc; // FIXME: make sure 32-bit relocs are used
520 <                WriteMacInt32(proc_area +  6, clip_data.data.size());
520 >                WriteMacInt32(proc_area +  6, data.size());
521                  WriteMacInt32(proc_area + 12, type);
522                  WriteMacInt32(proc_area + 18, scrap_area);
523                  we_put_this_data = true;
# Line 525 | Line 540 | void ClipboardSelectionClear(XSelectionC
540                  return;
541  
542          D(bug("Selection cleared, lost clipboard ownership\n"));
543 <        clip_data.type = 0;
543 >        clip_data.type = None;
544          clip_data.data.clear();
545   }
546  
# Line 554 | Line 569 | static bool handle_selection_TARGETS(XSe
569          targets.push_back(xa_timestamp);
570  
571          // Extra targets matchin current clipboard data
572 <        switch (clip_data.type) {
573 <        case FOURCC('T','E','X','T'):
559 <                targets.push_back(XA_STRING);
560 <                break;
561 <        case FOURCC('P','I','C','T'):
562 <                break;
563 <        }
572 >        if (clip_data.type != None)
573 >                targets.push_back(clip_data.type);
574  
575          // Change requestor property
576          XChangeProperty(x_display, req->requestor, req->property,
# Line 575 | Line 585 | static bool handle_selection_STRING(XSel
585          // Make sure we have valid data to send though ICCCM compliant
586          // clients should have first requested TARGETS to identify
587          // this possibility.
588 <        if (clip_data.type != FOURCC('T','E','X','T'))
588 >        if (clip_data.type != XA_STRING)
589                  return false;
590  
591          // Send the string, it's already encoded as ISO-8859-1
# Line 681 | Line 691 | void ClipboardSelectionRequest(XSelectio
691  
692          handle_selection(req, false);
693   }
684
685 void ClipboardSelectionNotify(XSelectionEvent *req)
686 {
687        if (req->requestor != clip_win || req->selection != xa_clipboard)
688                return;
689
690        D(bug("Selection is now available\n"));
691        memcpy(&xselection_event, req, sizeof(xselection_event));
692        LOCK_CLIPBOARD;
693        xselection_event_avail = true;
694        UNLOCK_CLIPBOARD;
695 }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines