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.7 by gbeauche, 2004-01-10T08:46:57Z

# 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 + *  TODO:
50 + *    - handle 'PICT' to image/png, image/ppm, PIXMAP (prefs order)
51 + *    - handle 'styl' to text/richtext (OOo Writer)
52 + *    - patch ZeroScrap so that we know when cached 'styl' is stale?
53 + */
54 +
55   #include "sysdeps.h"
56  
57   #include <X11/Xlib.h>
58   #include <X11/Xatom.h>
59   #include <pthread.h>
26 #include <set>
60   #include <vector>
61  
62   #include "macos_util.h"
# Line 37 | Line 70
70   #include "debug.h"
71  
72   #ifndef NO_STD_NAMESPACE
40 using std::set;
73   using std::vector;
74   #endif
75  
76  
45 // Do we replace PutScrap()?
46 #define REPLACE_PUTSCRAP 1
47
48 // Do we replace GetScrap()?
49 #define REPLACE_GETSCRAP 1
50
51 // Do we want PutScrap() to ignore requests from klipper?
52 #define PUTSCRAP_IGNORES_KLIPPER 0
53
77   // Do we want GetScrap() to check for TIMESTAMP and optimize out clipboard syncs?
78   #define GETSCRAP_REQUESTS_TIMESTAMP 0
79  
# Line 122 | Line 145 | struct ByteArray : public vector<uint8>
145          uint8 *data() { return &at(0); }
146   };
147  
148 < // 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
148 > // Clipboard data for requestors
149   struct ClipboardData {
150          Time time;
151 <        uint32 type;
151 >        Atom type;
152          ByteArray data;
153   };
154   static ClipboardData clip_data;
155  
156 + // Prototypes
157 + static void do_putscrap(uint32 type, void *scrap, int32 length);
158 + static void do_getscrap(void **handle, uint32 type, int32 offset);
159 +
160  
161   /*
162   *  Read an X11 property (hack from QT 3.1.2)
# Line 231 | Line 244 | static bool read_property(Display *dpy,
244   */
245  
246   static const uint64 SELECTION_MAX_WAIT = 500000; // 500 ms
234 static volatile bool xselection_event_avail;
235 static XSelectionEvent xselection_event;
247  
248 < static bool wait_for_selection_notify_event(Display *dpy, Window win, int timeout)
248 > static bool wait_for_selection_notify_event(Display *dpy, Window win, XEvent *event, int timeout)
249   {
250          uint64 start = GetTicks_usec();
251  
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
252          do {
253                  // Wait
254 <                struct timespec req = {0, 16666667};
255 <                nanosleep(&req, NULL);
254 >                XDisplayUnlock();
255 >                Delay_usec(5000);
256 >                XDisplayLock();
257  
258 <                // Return immediately if the event is now available
259 <                if (xselection_event_avail)
258 >                // Check for SelectionNotify event
259 >                if (XCheckTypedWindowEvent(dpy, win, SelectionNotify, event))
260                          return true;
261  
262          } while ((GetTicks_usec() - start) < timeout);
# Line 268 | Line 266 | static bool wait_for_selection_notify_ev
266  
267  
268   /*
271 *  Check for a "klipper" window which, in older versions (3.1), was
272 *  polling and retrieving clipboard data several times per second.
273 */
274
275 static bool is_klipper_window_probe(Window w);
276 static bool is_klipper_window_check(Window w);
277 static bool (*is_klipper_window)(Window w) = is_klipper_window_probe;
278 static Window klipper_win = None;
279
280 static bool is_klipper_window_probe(Window w)
281 {
282        // We expect "klipper" to be within the first clients to request
283        // data from our clipboard
284        static int clients_countdown = 2;
285
286        bool found = false;
287        char *window_name;
288        if (XFetchName(x_display, w, &window_name) && !strcmp(window_name, "klipper")) {
289                D(bug(" found and ignore clipboard requests from klipper window id: 0x%08x\n", w));
290                klipper_win = w;
291                found = true;
292        }
293
294        if (found || --clients_countdown <= 0)
295                is_klipper_window = is_klipper_window_check;
296 }
297
298 static bool is_klipper_window_check(Window w)
299 {
300        return w == klipper_win;
301 }
302
303
304 /*
269   *  Initialization
270   */
271  
# Line 343 | Line 307 | void ClipExit(void)
307   void PutScrap(uint32 type, void *scrap, int32 length)
308   {
309          D(bug("PutScrap type %08lx, data %p, length %ld\n", type, scrap, length));
346        if (!REPLACE_PUTSCRAP)
347                return;
310          if (we_put_this_data) {
311                  we_put_this_data = false;
312                  return;
# Line 352 | Line 314 | void PutScrap(uint32 type, void *scrap,
314          if (length <= 0)
315                  return;
316  
317 <        bool did_putscrap = false;
317 >        XDisplayLock();
318 >        do_putscrap(type, scrap, length);
319 >        XDisplayUnlock();
320 > }
321 >
322 > static void do_putscrap(uint32 type, void *scrap, int32 length)
323 > {
324 >        clip_data.type = None;
325          switch (type) {
326 <                case FOURCC('T','E','X','T'):
327 <                        D(bug(" clipping TEXT\n"));
328 <                        clip_data.type = type;
329 <                        clip_data.data.clear();
330 <                        clip_data.data.reserve(length);
331 <
332 <                        // Convert text from Mac charset to ISO-Latin1
333 <                        uint8 *p = (uint8 *)scrap;
334 <                        for (int i=0; i<length; i++) {
335 <                                uint8 c = *p++;
336 <                                if (c < 0x80) {
337 <                                        if (c == 13)    // CR -> LF
338 <                                                c = 10;
339 <                                } else if (!no_clip_conversion)
340 <                                        c = mac2iso[c & 0x7f];
341 <                                clip_data.data.push_back(c);
342 <                        }
343 <                        did_putscrap = true;
344 <                        break;
326 >        case FOURCC('T','E','X','T'): {
327 >                D(bug(" clipping TEXT\n"));
328 >                clip_data.type = XA_STRING;
329 >                clip_data.data.clear();
330 >                clip_data.data.reserve(length);
331 >
332 >                // Convert text from Mac charset to ISO-Latin1
333 >                uint8 *p = (uint8 *)scrap;
334 >                for (int i=0; i<length; i++) {
335 >                        uint8 c = *p++;
336 >                        if (c < 0x80) {
337 >                                if (c == 13)    // CR -> LF
338 >                                        c = 10;
339 >                        } else if (!no_clip_conversion)
340 >                                c = mac2iso[c & 0x7f];
341 >                        clip_data.data.push_back(c);
342 >                }
343 >                break;
344 >        }
345 >
346 >        case FOURCC('s','t','y','l'): {
347 >                D(bug(" clipping styl\n"));
348 >                uint16 *p = (uint16 *)scrap;
349 >                uint16 n = ntohs(*p++);
350 >                D(bug(" %d styles (%d bytes)\n", n, length));
351 >                for (int i = 0; i < n; i++) {
352 >                        uint32 offset = ntohl(*(uint32 *)p); p += 2;
353 >                        uint16 line_height = ntohs(*p++);
354 >                        uint16 font_ascent = ntohs(*p++);
355 >                        uint16 font_family = ntohs(*p++);
356 >                        uint16 style_code = ntohs(*p++);
357 >                        uint16 char_size = ntohs(*p++);
358 >                        uint16 r = ntohs(*p++);
359 >                        uint16 g = ntohs(*p++);
360 >                        uint16 b = ntohs(*p++);
361 >                        D(bug("  offset=%d, height=%d, font ascent=%d, id=%d, style=%x, size=%d, RGB=%x/%x/%x\n",
362 >                                  offset, line_height, font_ascent, font_family, style_code, char_size, r, g, b));
363 >                }
364 >                break;
365 >        }
366          }
367  
368          // Acquire selection ownership
369 <        if (did_putscrap) {
369 >        if (clip_data.type != None) {
370                  clip_data.time = CurrentTime;
371                  while (XGetSelectionOwner(x_display, xa_clipboard) != clip_win)
372                          XSetSelectionOwner(x_display, xa_clipboard, clip_win, clip_data.time);
# Line 391 | Line 381 | void PutScrap(uint32 type, void *scrap,
381   void GetScrap(void **handle, uint32 type, int32 offset)
382   {
383          D(bug("GetScrap handle %p, type %08x, offset %d\n", handle, type, offset));
384 <        if (!REPLACE_GETSCRAP)
384 >
385 >        XDisplayLock();
386 >        do_getscrap(handle, type, offset);
387 >        XDisplayUnlock();
388 > }
389 >
390 > static void do_getscrap(void **handle, uint32 type, int32 offset)
391 > {
392 >        ByteArray data;
393 >        XEvent event;
394 >
395 >        // If we own the selection, the data is already available on MacOS side
396 >        if (XGetSelectionOwner(x_display, xa_clipboard) == clip_win)
397                  return;
398  
399          // Check TIMESTAMP
400   #if GETSCRAP_REQUESTS_TIMESTAMP
401          static Time last_timestamp = 0;
402          XConvertSelection(x_display, xa_clipboard, xa_timestamp, xa_clipboard, clip_win, CurrentTime);
403 <        if (wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) &&
404 <                xselection_event.property != None) {
405 <                ByteArray data;
406 <                if (read_property(x_display,
407 <                                                  xselection_event.requestor, xselection_event.property,
408 <                                                  true, data, 0, 0, 0, false)) {
409 <                        Time timestamp = ((long *)data.data())[0];
410 <                        if (timestamp == last_timestamp)
409 <                                return;
410 <                }
403 >        if (wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) &&
404 >                event.xselection.property != None &&
405 >                read_property(x_display,
406 >                                          event.xselection.requestor, event.xselection.property,
407 >                                          true, data, 0, 0, 0, false)) {
408 >                Time timestamp = ((long *)data.data())[0];
409 >                if (timestamp <= last_timestamp)
410 >                        return;
411          }
412 +        last_timestamp = CurrentTime;
413   #endif
414  
415          // Get TARGETS available
416   #if GETSCRAP_REQUESTS_TARGETS
417          XConvertSelection(x_display, xa_clipboard, xa_targets, xa_clipboard, clip_win, CurrentTime);
418 <        if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
419 <                xselection_event.property == None)
420 <                return;
421 <
421 <        ByteArray data;
422 <        if (!read_property(x_display,
423 <                                           xselection_event.requestor, xselection_event.property,
418 >        if (!wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) ||
419 >                event.xselection.property == None ||
420 >                !read_property(x_display,
421 >                                           event.xselection.requestor, event.xselection.property,
422                                             true, data, 0, 0, 0, false))
423                  return;
424   #endif
# Line 432 | Line 430 | void GetScrap(void **handle, uint32 type
430          long *atoms = (long *)data.data();
431          for (int i = 0; i < n_atoms; i++) {
432                  Atom target = atoms[i];
433 +                D(bug("  target %08x (%s)\n", target, XGetAtomName(x_display, target)));
434                  switch (type) {
435                  case FOURCC('T','E','X','T'):
436                          D(bug(" clipping TEXT\n"));
# Line 457 | Line 456 | void GetScrap(void **handle, uint32 type
456  
457          // Get the native clipboard data
458          XConvertSelection(x_display, xa_clipboard, format, xa_clipboard, clip_win, CurrentTime);
459 <        if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
460 <                xselection_event.property == None ||
459 >        if (!wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) ||
460 >                event.xselection.property == None ||
461                  !read_property(x_display,
462 <                                           xselection_event.requestor, xselection_event.property,
463 <                                           false, clip_data.data, 0, 0, 0, format == XA_STRING))
462 >                                           event.xselection.requestor, event.xselection.property,
463 >                                           false, data, 0, 0, 0, format == XA_STRING))
464                  return;
465  
467        clip_data.type = type;
468        clip_data.time = xselection_event.time;
469
466          // Allocate space for new scrap in MacOS side
467          M68kRegisters r;
468 <        r.d[0] = clip_data.data.size();
468 >        r.d[0] = data.size();
469          Execute68kTrap(0xa71e, &r);                     // NewPtrSysClear()
470          uint32 scrap_area = r.a[0];
471  
# Line 478 | Line 474 | void GetScrap(void **handle, uint32 type
474                  case FOURCC('T','E','X','T'):
475                          // Convert text from ISO-Latin1 to Mac charset
476                          uint8 *p = Mac2HostAddr(scrap_area);
477 <                        for (int i = 0; i < clip_data.data.size(); i++) {
478 <                                uint8 c = clip_data.data[i];
477 >                        for (int i = 0; i < data.size(); i++) {
478 >                                uint8 c = data[i];
479                                  if (c < 0x80) {
480                                          if (c == 10)    // LF -> CR
481                                                  c = 13;
# Line 491 | Line 487 | void GetScrap(void **handle, uint32 type
487                  }
488  
489                  // Add new data to clipboard
490 <                static uint8 proc[] = {
491 <                        0x59, 0x8f,                                     // subq.l       #4,sp
492 <                        0xa9, 0xfc,                                     // ZeroScrap()
493 <                        0x2f, 0x3c, 0, 0, 0, 0,         // move.l       #length,-(sp)
494 <                        0x2f, 0x3c, 0, 0, 0, 0,         // move.l       #type,-(sp)
495 <                        0x2f, 0x3c, 0, 0, 0, 0,         // move.l       #outbuf,-(sp)
496 <                        0xa9, 0xfe,                                     // PutScrap()
497 <                        0x58, 0x8f,                                     // addq.l       #4,sp
498 <                        M68K_RTS >> 8, M68K_RTS
490 >                static uint16 proc[] = {
491 >                        PW(0x598f),                                     // subq.l       #4,sp
492 >                        PW(0xa9fc),                                     // ZeroScrap()
493 >                        PW(0x2f3c), 0, 0,                       // move.l       #length,-(sp)
494 >                        PW(0x2f3c), 0, 0,                       // move.l       #type,-(sp)
495 >                        PW(0x2f3c), 0, 0,                       // move.l       #outbuf,-(sp)
496 >                        PW(0xa9fe),                                     // PutScrap()
497 >                        PW(0x588f),                                     // addq.l       #4,sp
498 >                        PW(M68K_RTS)
499                  };
500 <                uint32 proc_area = (uint32)proc; // FIXME: make sure 32-bit relocs are used
501 <                WriteMacInt32(proc_area +  6, clip_data.data.size());
500 >                uint32 proc_area = (uint32)proc;
501 >                WriteMacInt32(proc_area +  6, data.size());
502                  WriteMacInt32(proc_area + 12, type);
503                  WriteMacInt32(proc_area + 18, scrap_area);
504                  we_put_this_data = true;
# Line 525 | Line 521 | void ClipboardSelectionClear(XSelectionC
521                  return;
522  
523          D(bug("Selection cleared, lost clipboard ownership\n"));
524 <        clip_data.type = 0;
524 >        clip_data.type = None;
525          clip_data.data.clear();
526   }
527  
# Line 554 | Line 550 | static bool handle_selection_TARGETS(XSe
550          targets.push_back(xa_timestamp);
551  
552          // Extra targets matchin current clipboard data
553 <        switch (clip_data.type) {
554 <        case FOURCC('T','E','X','T'):
559 <                targets.push_back(XA_STRING);
560 <                break;
561 <        case FOURCC('P','I','C','T'):
562 <                break;
563 <        }
553 >        if (clip_data.type != None)
554 >                targets.push_back(clip_data.type);
555  
556          // Change requestor property
557          XChangeProperty(x_display, req->requestor, req->property,
# Line 575 | Line 566 | static bool handle_selection_STRING(XSel
566          // Make sure we have valid data to send though ICCCM compliant
567          // clients should have first requested TARGETS to identify
568          // this possibility.
569 <        if (clip_data.type != FOURCC('T','E','X','T'))
569 >        if (clip_data.type != XA_STRING)
570                  return false;
571  
572          // Send the string, it's already encoded as ISO-8859-1
# Line 667 | Line 658 | void ClipboardSelectionRequest(XSelectio
658          if (req->requestor == clip_win || req->selection != xa_clipboard)
659                  return;
660  
670 #if PUTSCRAP_IGNORES_KLIPPER
671        if (is_klipper_window(req->requestor))
672                return;
673 #endif
674
661          D(bug("Selection requested from 0x%lx to 0x%lx (%s) 0x%lx (%s)\n",
662                    req->requestor,
663                    req->selection,
# Line 681 | Line 667 | void ClipboardSelectionRequest(XSelectio
667  
668          handle_selection(req, false);
669   }
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