themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_cocoa.mm
bloba24f93dd0d149be97434cb68fe8ce2bd6f8336c1
1 //
2 // "$Id: Fl_cocoa.mm 8807 2011-06-16 12:35:32Z manolo $"
3 //
4 // MacOS-Cocoa specific code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2011 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 //     http://www.fltk.org/str.php
28 //// From the inner edge of a MetroWerks CodeWarrior CD:
29 // (without permission)
31 // "Three Compiles for 68Ks under the sky,
32 // Seven Compiles for PPCs in their fragments of code,
33 // Nine Compiles for Mortal Carbon doomed to die,
34 // One Compile for Mach-O Cocoa on its Mach-O throne,
35 // in the Land of MacOS X where the Drop-Shadows lie.
36 // 
37 // One Compile to link them all, One Compile to merge them,
38 // One Compile to copy them all and in the bundle bind them,
39 // in the Land of MacOS X where the Drop-Shadows lie."
41 #ifdef __APPLE__
43 #define CONSOLIDATE_MOTION 0
44 extern "C" {
45 #include <pthread.h>
49 #include <FL/Fl.H>
50 #include <FL/x.H>
51 #include <FL/Fl_Window.H>
52 #include <FL/Fl_Tooltip.H>
53 #include <FL/Fl_Sys_Menu_Bar.H>
54 #include <FL/Fl_Printer.H>
55 #include <FL/Fl_Input_.H>
56 #include <FL/Fl_Text_Display.H>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include "flstring.h"
60 #include <unistd.h>
61 #include <stdarg.h>
63 #import <Cocoa/Cocoa.h>
65 #ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h
66 #if defined(__LP64__) && __LP64__
67 typedef long NSInteger;
68 typedef unsigned long NSUInteger;
69 #else
70 typedef long NSInteger;
71 typedef unsigned int NSUInteger;
72 #endif
73 #endif
76 // #define DEBUG_SELECT         // UNCOMMENT FOR SELECT()/THREAD DEBUGGING
77 #ifdef DEBUG_SELECT
78 #include <stdio.h>              // testing
79 #define DEBUGMSG(msg)           if ( msg ) fprintf(stderr, msg);
80 #define DEBUGPERRORMSG(msg)     if ( msg ) perror(msg)
81 #define DEBUGTEXT(txt)          txt
82 #else
83 #define DEBUGMSG(msg)
84 #define DEBUGPERRORMSG(msg)
85 #define DEBUGTEXT(txt)          NULL
86 #endif /*DEBUG_SELECT*/
88 // external functions
89 extern void fl_fix_focus();
90 extern Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h);
92 // forward definition of functions in this file
93 // converting cr lf converter function
94 static void convert_crlf(char * string, size_t len);
95 static void createAppleMenu(void);
96 static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h);
97 static void cocoaMouseHandler(NSEvent *theEvent);
99 static Fl_Quartz_Graphics_Driver fl_quartz_driver;
100 static Fl_Display_Device fl_quartz_display(&fl_quartz_driver);
101 FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_quartz_driver; // the current target device of graphics operations
102 Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_quartz_display; // the current target surface of graphics operations
103 Fl_Display_Device *Fl_Display_Device::_display = &fl_quartz_display; // the platform display
105 // public variables
106 int fl_screen;
107 CGContextRef fl_gc = 0;
108 void *fl_system_menu;                   // this is really a NSMenu*
109 Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0;
110 void *fl_default_cursor;                // this is really a NSCursor*
111 void *fl_capture = 0;                   // (NSWindow*) we need this to compensate for a missing(?) mouse capture
112 bool fl_show_iconic;                    // true if called from iconize() - shows the next created window in collapsed state
113 //int fl_disable_transient_for;           // secret method of removing TRANSIENT_FOR
114 Window fl_window;
115 Fl_Window *Fl_Window::current_;
116 int fl_mac_os_version = 0;              // the version number of the running Mac OS X (e.g., 100604 for 10.6.4)
118 // forward declarations of variables in this file
119 static int got_events = 0;
120 static Fl_Window* resize_from_system;
122 #if CONSOLIDATE_MOTION
123 static Fl_Window* send_motion;
124 extern Fl_Window* fl_xmousewin;
125 #endif
127 enum { FLTKTimerEvent = 1, FLTKDataReadyEvent };
130 /* fltk-utf8 placekeepers */
131 void fl_reset_spot()
135 void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
139 void fl_set_status(int x, int y, int w, int h)
144  * Mac keyboard lookup table
145  * See also the inverse converter vktab in Fl_get_key_mac.cxx
146  */
147 static unsigned short macKeyLookUp[128] =
149   'a', 's', 'd', 'f', 'h', 'g', 'z', 'x',
150   'c', 'v', '^', 'b', 'q', 'w', 'e', 'r',
151   
152   'y', 't', '1', '2', '3', '4', '6', '5',
153   '=', '9', '7', '-', '8', '0', ']', 'o',
154   
155   'u', '[', 'i', 'p', FL_Enter, 'l', 'j', '\'',
156   'k', ';', '\\', ',', '/', 'n', 'm', '.',
157   
158   FL_Tab, ' ', '`', FL_BackSpace, 
159   FL_KP_Enter, FL_Escape, FL_Meta_R, FL_Meta_L,
160   FL_Shift_L, FL_Caps_Lock, FL_Alt_L, FL_Control_L, 
161   FL_Shift_R, FL_Alt_R, FL_Control_R, 0/*FL_F*/,
162   
163   0, FL_KP+'.', FL_Right, FL_KP+'*', 0, FL_KP+'+', FL_Left, FL_Num_Lock,
164   FL_Down, 0, 0, FL_KP+'/', FL_KP_Enter, FL_Up, FL_KP+'-', 0,
165   
166   0, FL_KP+'=', FL_KP+'0', FL_KP+'1', FL_KP+'2', FL_KP+'3', FL_KP+'4', FL_KP+'5',
167   FL_KP+'6', FL_KP+'7', 0, FL_KP+'8', FL_KP+'9', 0, 0, 0,
168   
169   FL_F+5, FL_F+6, FL_F+7, FL_F+3, FL_F+8, FL_F+9, 0, FL_F+11,
170   0, FL_F+13, FL_F+16, FL_F+14, 0, FL_F+10, FL_Menu, FL_F+12,
171   
172   0, FL_F+15, FL_Help, FL_Home, FL_Page_Up, FL_Delete, FL_F+4, FL_End,
173   FL_F+2, FL_Page_Down, FL_F+1, FL_Left, FL_Right, FL_Down, FL_Up, 0/*FL_Power*/,
177  * convert the current mouse chord into the FLTK modifier state
178  */
179 static unsigned int mods_to_e_state( NSUInteger mods )
181   long state = 0;
182   if ( mods & NSCommandKeyMask ) state |= FL_META;
183   if ( mods & NSAlternateKeyMask ) state |= FL_ALT;
184   if ( mods & NSControlKeyMask ) state |= FL_CTRL;
185   if ( mods & NSShiftKeyMask ) state |= FL_SHIFT;
186   if ( mods & NSAlphaShiftKeyMask ) state |= FL_CAPS_LOCK;
187   unsigned int ret = ( Fl::e_state & 0xff000000 ) | state;
188   Fl::e_state = ret;
189   //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods );
190   return ret;
193 // these pointers are set by the Fl::lock() function:
194 static void nothing() {}
195 void (*fl_lock_function)() = nothing;
196 void (*fl_unlock_function)() = nothing;
199 // Select interface -- how it's implemented:
200 //     When the user app configures one or more file descriptors to monitor
201 //     with Fl::add_fd(), we start a separate thread to select() the  data,
202 //     sending a custom OSX 'FLTK data ready event' to the parent  thread's
203 //     RunApplicationLoop(), so that it triggers the data  ready  callbacks
204 //     in the parent thread.                               -erco 04/04/04
205 //     
206 #define POLLIN  1
207 #define POLLOUT 4
208 #define POLLERR 8
210 // Class to handle select() 'data ready'
211 class DataReady
213   struct FD
214   {
215     int fd;
216     short events;
217     void (*cb)(int, void*);
218     void* arg;
219   };
220   int nfds, fd_array_size;
221   FD *fds;
222   pthread_t tid;                // select()'s thread id
223   
224   // Data that needs to be locked (all start with '_')
225   pthread_mutex_t _datalock;    // data lock
226   fd_set _fdsets[3];            // r/w/x sets user wants to monitor
227   int _maxfd;                   // max fd count to monitor
228   int _cancelpipe[2];           // pipe used to help cancel thread
229   
230 public:
231   DataReady()
232   {
233     nfds = 0;
234     fd_array_size = 0;
235     fds = 0;
236     tid = 0;
237     
238     pthread_mutex_init(&_datalock, NULL);
239     FD_ZERO(&_fdsets[0]); FD_ZERO(&_fdsets[1]); FD_ZERO(&_fdsets[2]);
240     _cancelpipe[0] = _cancelpipe[1] = 0;
241     _maxfd = -1;
242   }
243   
244   ~DataReady()
245   {
246     CancelThread(DEBUGTEXT("DESTRUCTOR\n"));
247     if (fds) { free(fds); fds = 0; }
248     nfds = 0;
249   }
250   
251   // Locks
252   //    The convention for locks: volatile vars start with '_',
253   //    and must be locked before use. Locked code is prefixed 
254   //    with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco
255   //
256   void DataLock() { pthread_mutex_lock(&_datalock); }
257   void DataUnlock() { pthread_mutex_unlock(&_datalock); }
258   
259   // Accessors
260   int IsThreadRunning() { return(tid ? 1 : 0); }
261   int GetNfds() { return(nfds); }
262   int GetCancelPipe(int ix) { return(_cancelpipe[ix]); }
263   fd_set GetFdset(int ix) { return(_fdsets[ix]); }
264   
265   // Methods
266   void AddFD(int n, int events, void (*cb)(int, void*), void *v);
267   void RemoveFD(int n, int events);
268   int CheckData(fd_set& r, fd_set& w, fd_set& x);
269   void HandleData(fd_set& r, fd_set& w, fd_set& x);
270   static void* DataReadyThread(void *self);
271   void StartThread(void);
272   void CancelThread(const char *reason);
275 static DataReady dataready;
277 void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v)
279   RemoveFD(n, events);
280   int i = nfds++;
281   if (i >= fd_array_size) 
282   {
283     fl_open_display(); // necessary for NSApp to be defined and the event loop to work
284     FD *temp;
285     fd_array_size = 2*fd_array_size+1;
286     if (!fds) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); }
287     else { temp = (FD*)realloc(fds, fd_array_size*sizeof(FD)); }
288     if (!temp) return;
289     fds = temp;
290   }
291   fds[i].cb  = cb;
292   fds[i].arg = v;
293   fds[i].fd  = n;
294   fds[i].events = events;
295   DataLock();
296   /*LOCK*/  if (events & POLLIN)  FD_SET(n, &_fdsets[0]);
297   /*LOCK*/  if (events & POLLOUT) FD_SET(n, &_fdsets[1]);
298   /*LOCK*/  if (events & POLLERR) FD_SET(n, &_fdsets[2]);
299   /*LOCK*/  if (n > _maxfd) _maxfd = n;
300   DataUnlock();
303 // Remove an FD from the array
304 void DataReady::RemoveFD(int n, int events)
306   int i,j;
307   _maxfd = -1; // recalculate maxfd on the fly
308   for (i=j=0; i<nfds; i++) {
309     if (fds[i].fd == n) {
310       int e = fds[i].events & ~events;
311       if (!e) continue; // if no events left, delete this fd
312       fds[i].events = e;
313     }
314     if (fds[i].fd > _maxfd) _maxfd = fds[i].fd;
315     // move it down in the array if necessary:
316     if (j<i) {
317       fds[j] = fds[i];
318     }
319     j++;
320   }
321   nfds = j;
322   DataLock();
323   /*LOCK*/  if (events & POLLIN)  FD_CLR(n, &_fdsets[0]);
324   /*LOCK*/  if (events & POLLOUT) FD_CLR(n, &_fdsets[1]);
325   /*LOCK*/  if (events & POLLERR) FD_CLR(n, &_fdsets[2]);
326   DataUnlock();
329 // CHECK IF USER DATA READY, RETURNS r/w/x INDICATING WHICH IF ANY
330 int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x)
332   int ret;
333   DataLock();
334   /*LOCK*/  timeval t = { 0, 1 };               // quick check
335   /*LOCK*/  r = _fdsets[0], w = _fdsets[1], x = _fdsets[2];
336   /*LOCK*/  ret = ::select(_maxfd+1, &r, &w, &x, &t);
337   DataUnlock();
338   if ( ret == -1 ) {
339     DEBUGPERRORMSG("CheckData(): select()");
340   }
341   return(ret);
344 // HANDLE DATA READY CALLBACKS
345 void DataReady::HandleData(fd_set& r, fd_set& w, fd_set& x)
347   for (int i=0; i<nfds; i++) {
348     int f = fds[i].fd;
349     short revents = 0;
350     if (FD_ISSET(f, &r)) revents |= POLLIN;
351     if (FD_ISSET(f, &w)) revents |= POLLOUT;
352     if (FD_ISSET(f, &x)) revents |= POLLERR;
353     if (fds[i].events & revents) {
354       DEBUGMSG("DOING CALLBACK: ");
355       fds[i].cb(f, fds[i].arg);
356       DEBUGMSG("DONE\n");
357     }
358   }
361 // DATA READY THREAD
362 //    This thread watches for changes in user's file descriptors.
363 //    Sends a 'data ready event' to the main thread if any change.
365 void* DataReady::DataReadyThread(void *o)
367   DataReady *self = (DataReady*)o;
368   NSAutoreleasePool *localPool;
369   localPool = [[NSAutoreleasePool alloc] init]; 
370   while ( 1 ) {                                 // loop until thread cancel or error
371     // Thread safe local copies of data before each select()
372     self->DataLock();
373     /*LOCK*/  int maxfd = self->_maxfd;
374     /*LOCK*/  fd_set r = self->GetFdset(0);
375     /*LOCK*/  fd_set w = self->GetFdset(1);
376     /*LOCK*/  fd_set x = self->GetFdset(2);
377     /*LOCK*/  int cancelpipe = self->GetCancelPipe(0);
378     /*LOCK*/  if ( cancelpipe > maxfd ) maxfd = cancelpipe;
379     /*LOCK*/  FD_SET(cancelpipe, &r);           // add cancelpipe to fd's to watch
380     /*LOCK*/  FD_SET(cancelpipe, &x);
381     self->DataUnlock();
382     // timeval t = { 1000, 0 }; // 1000 seconds;
383     timeval t = { 2, 0 };       // HACK: 2 secs prevents 'hanging' problem
384     int ret = ::select(maxfd+1, &r, &w, &x, &t);
385     pthread_testcancel();       // OSX 10.0.4 and older: needed for parent to cancel
386     switch ( ret ) {
387       case 0:   // NO DATA
388         continue;
389       case -1:  // ERROR
390       {
391         DEBUGPERRORMSG("CHILD THREAD: select() failed");
392         return(NULL);           // error? exit thread
393       }
394       default:  // DATA READY
395       {
396         if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x))       // cancel?
397           { return(NULL); }                                             // just exit
398         DEBUGMSG("CHILD THREAD: DATA IS READY\n");
399         NSPoint pt={0,0};
400         NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt 
401                                        modifierFlags:0
402                                            timestamp:0
403                                         windowNumber:0 context:NULL 
404                                              subtype:FLTKDataReadyEvent data1:0 data2:0];
405         [NSApp postEvent:event atStart:NO];
406         return(NULL);           // done with thread
407       }
408     }
409   }
412 // START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE
413 void DataReady::StartThread(void)
415   CancelThread(DEBUGTEXT("STARTING NEW THREAD\n"));
416   DataLock();
417   /*LOCK*/  pipe(_cancelpipe);  // pipe for sending cancel msg to thread
418   DataUnlock();
419   DEBUGMSG("*** START THREAD\n");
420   pthread_create(&tid, NULL, DataReadyThread, (void*)this);
423 // CANCEL 'DATA READY' THREAD, CLOSE PIPE
424 void DataReady::CancelThread(const char *reason)
426   if ( tid ) {
427     DEBUGMSG("*** CANCEL THREAD: ");
428     DEBUGMSG(reason);
429     if ( pthread_cancel(tid) == 0 ) {           // cancel first
430       DataLock();
431       /*LOCK*/  write(_cancelpipe[1], "x", 1);  // wake thread from select
432       DataUnlock();
433       pthread_join(tid, NULL);                  // wait for thread to finish
434     }
435     tid = 0;
436     DEBUGMSG("(JOINED) OK\n");
437   }
438   // Close pipe if open
439   DataLock();
440   /*LOCK*/  if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; }
441   /*LOCK*/  if ( _cancelpipe[1] ) { close(_cancelpipe[1]); _cancelpipe[1] = 0; }
442   DataUnlock();
445 void Fl::add_fd( int n, int events, void (*cb)(int, void*), void *v )
447   dataready.AddFD(n, events, cb, v);
450 void Fl::add_fd(int fd, void (*cb)(int, void*), void* v)
452   dataready.AddFD(fd, POLLIN, cb, v);
455 void Fl::remove_fd(int n, int events)
457   dataready.RemoveFD(n, events);
460 void Fl::remove_fd(int n)
462   dataready.RemoveFD(n, -1);
466  * Check if there is actually a message pending!
467  */
468 int fl_ready()
470   NSEvent *retval = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0]
471                                     inMode:NSDefaultRunLoopMode dequeue:NO];
472   return retval != nil;
476 static void processFLTKEvent(void) {
477   fl_lock_function();
478   dataready.CancelThread(DEBUGTEXT("DATA READY EVENT\n"));
479   
480   // CHILD THREAD TELLS US DATA READY
481   //     Check to see what's ready, and invoke user's cb's
482   //
483   fd_set r,w,x;
484   switch(dataready.CheckData(r,w,x)) {
485     case 0:     // NO DATA
486       break;
487     case -1:    // ERROR
488       break;
489     default:    // DATA READY
490       dataready.HandleData(r,w,x);
491       break;
492   }
493   fl_unlock_function();
494   return;
499  * break the current event loop
500  */
501 static void breakMacEventLoop()
503   fl_lock_function();
504   
505   NSPoint pt={0,0};
506   NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt 
507                                  modifierFlags:0
508                                      timestamp:0
509                                   windowNumber:0 context:NULL 
510                                        subtype:FLTKTimerEvent data1:0 data2:0];
511   [NSApp postEvent:event atStart:NO];
512   fl_unlock_function();
516 // MacOS X timers
519 struct MacTimeout {
520   Fl_Timeout_Handler callback;
521   void* data;
522   CFRunLoopTimerRef timer;
523   char pending; 
525 static MacTimeout* mac_timers;
526 static int mac_timer_alloc;
527 static int mac_timer_used;
529 static void realloc_timers()
531   if (mac_timer_alloc == 0) {
532     mac_timer_alloc = 8;
533     fl_open_display(); // needed because the timer creates an event
534   }
535   mac_timer_alloc *= 2;
536   MacTimeout* new_timers = new MacTimeout[mac_timer_alloc];
537   memset(new_timers, 0, sizeof(MacTimeout)*mac_timer_alloc);
538   memcpy(new_timers, mac_timers, sizeof(MacTimeout) * mac_timer_used);
539   MacTimeout* delete_me = mac_timers;
540   mac_timers = new_timers;
541   delete [] delete_me;
544 static void delete_timer(MacTimeout& t)
546   if (t.timer) {
547     CFRunLoopRemoveTimer(CFRunLoopGetCurrent(),
548                       t.timer,
549                       kCFRunLoopDefaultMode);
550     CFRelease(t.timer);
551     memset(&t, 0, sizeof(MacTimeout));
552   }
555 static void do_timer(CFRunLoopTimerRef timer, void* data)
557   for (int i = 0;  i < mac_timer_used;  ++i) {
558     MacTimeout& t = mac_timers[i];
559     if (t.timer == timer  &&  t.data == data) {
560       t.pending = 0;
561       (*t.callback)(data);
562       if (t.pending==0)
563         delete_timer(t);
564       break;
565     }
566   }
567   breakMacEventLoop();
570 @interface FLWindow : NSWindow {
571   Fl_Window *w;
572   BOOL containsGLsubwindow;
574 - (FLWindow*)initWithFl_W:(Fl_Window *)flw 
575               contentRect:(NSRect)rect 
576                 styleMask:(NSUInteger)windowStyle;
577 - (Fl_Window *)getFl_Window;
578 - (BOOL)windowShouldClose:(FLWindow *)w;
579 - (BOOL)containsGLsubwindow;
580 - (void)setContainsGLsubwindow:(BOOL)contains;
581 @end
583 @implementation FLWindow
584 - (FLWindow*)initWithFl_W:(Fl_Window *)flw 
585               contentRect:(NSRect)rect 
586                 styleMask:(NSUInteger)windowStyle 
588   self = [super initWithContentRect:rect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO];
589   if (self) {
590     w = flw;
591     containsGLsubwindow = NO;
592   }
593   return self;
595 - (Fl_Window *)getFl_Window;
597   return w;
599 - (BOOL)windowShouldClose:(FLWindow *)fl
601   fl_lock_function();
602   Fl::handle( FL_CLOSE, [fl getFl_Window] ); // this might or might not close the window
603   if (!Fl_X::first) return YES;
604   Fl_Window *l = Fl::first_window();
605   while( l != NULL && l != [fl getFl_Window]) l = Fl::next_window(l);
606   fl_unlock_function();
607   return (l == NULL ? YES : NO);
609 - (BOOL)containsGLsubwindow
611   return containsGLsubwindow;
613 - (void)setContainsGLsubwindow:(BOOL)contains
615   containsGLsubwindow = contains;
617 @end
619 @interface FLApplication : NSObject
622 + (void)sendEvent:(NSEvent *)theEvent;
623 @end
626  * This function is the central event handler.
627  * It reads events from the event queue using the given maximum time
628  * Funny enough, it returns the same time that it got as the argument. 
629  */
630 static double do_queued_events( double time = 0.0 ) 
632   got_events = 0;
633   
634   // Check for re-entrant condition
635   if ( dataready.IsThreadRunning() ) {
636     dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n"));
637   }
638   
639   // Start thread to watch for data ready
640   if ( dataready.GetNfds() ) {
641     dataready.StartThread();
642   }
643   
644   fl_unlock_function();
645   NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask 
646                                       untilDate:[NSDate dateWithTimeIntervalSinceNow:time] 
647                                          inMode:NSDefaultRunLoopMode dequeue:YES];  
648   if (event != nil) {
649     got_events = 1;
650     [FLApplication sendEvent:event]; // will then call [NSApplication sendevent:]
651   }
652   fl_lock_function();
653   
654 #if CONSOLIDATE_MOTION
655   if (send_motion && send_motion == fl_xmousewin) {
656     send_motion = 0;
657     Fl::handle(FL_MOVE, fl_xmousewin);
658   }
659 #endif
660   
661   return time;
665  * This public function handles all events. It wait a maximum of 
666  * 'time' seconds for an event. This version returns 1 if events
667  * other than the timeout timer were processed.
669  * \todo there is no socket handling in this code whatsoever
670  */
671 int fl_wait( double time ) 
673   do_queued_events( time );
674   return (got_events);
677 double fl_mac_flush_and_wait(double time_to_wait, char in_idle) {
678   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
679   Fl::flush();
680   if (Fl::idle && !in_idle) // 'idle' may have been set within flush()
681     time_to_wait = 0.0;
682   double retval = fl_wait(time_to_wait);
683   [pool release];
684   return retval;
687 // updates Fl::e_x, Fl::e_y, Fl::e_x_root, and Fl::e_y_root
688 static void update_e_xy_and_e_xy_root(NSWindow *nsw)
690   NSPoint pt;
691   pt = [nsw mouseLocationOutsideOfEventStream];
692   Fl::e_x = int(pt.x);
693   Fl::e_y = int([[nsw contentView] frame].size.height - pt.y);
694   pt = [NSEvent mouseLocation];
695   Fl::e_x_root = int(pt.x);
696   Fl::e_y_root = int([[nsw screen] frame].size.height - pt.y);
700  * Cocoa Mousewheel handler
701  */
702 static void cocoaMouseWheelHandler(NSEvent *theEvent)
704   // Handle the new "MightyMouse" mouse wheel events. Please, someone explain
705   // to me why Apple changed the API on this even though the current API
706   // supports two wheels just fine. Matthias,
707   fl_lock_function();
708   
709   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
710   if ( !window->shown() ) {
711     fl_unlock_function();
712     return;
713   }
714   Fl::first_window(window);
715   
716   // Under OSX, single mousewheel increments are 0.1,
717   // so make sure they show up as at least 1..
718   //
719   float dx = [theEvent deltaX]; if ( fabs(dx) < 1.0 ) dx = (dx > 0) ? 1.0 : -1.0;
720   float dy = [theEvent deltaY]; if ( fabs(dy) < 1.0 ) dy = (dy > 0) ? 1.0 : -1.0;
721   if ([theEvent deltaX] != 0) {
722     Fl::e_dx = (int)-dx;
723     Fl::e_dy = 0;
724     if ( Fl::e_dx) Fl::handle( FL_MOUSEWHEEL, window );
725   } else if ([theEvent deltaY] != 0) {
726     Fl::e_dx = 0;
727     Fl::e_dy = (int)-dy;
728     if ( Fl::e_dy) Fl::handle( FL_MOUSEWHEEL, window );
729   } else {
730     fl_unlock_function();
731     return;
732   }
733   
734   fl_unlock_function();
735   
736   //  return noErr;
740  * Cocoa Mouse Button Handler
741  */
742 static void cocoaMouseHandler(NSEvent *theEvent)
744   static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2 };
745   static int px, py;
746   static char suppressed = 0;
747   
748   fl_lock_function();
749   
750   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
751   if ( !window->shown() ) {
752     fl_unlock_function();
753     return;
754   }
755   Fl_Window *first = Fl::first_window();
756   if (first != window && !(first->modal() || first->non_modal())) Fl::first_window(window);
757   NSPoint pos = [theEvent locationInWindow];
758   pos.y = window->h() - pos.y;
759   NSInteger btn = [theEvent buttonNumber]  + 1;
760   NSUInteger mods = [theEvent modifierFlags];  
761   int sendEvent = 0;
762   
763   NSEventType etype = [theEvent type];
764   if (etype == NSLeftMouseDown || etype == NSRightMouseDown || etype == NSOtherMouseDown) {
765     if (btn == 1) Fl::e_state |= FL_BUTTON1;
766     else if (btn == 3) Fl::e_state |= FL_BUTTON2;
767     else if (btn == 2) Fl::e_state |= FL_BUTTON3;
768   }
769   else if (etype == NSLeftMouseUp || etype == NSRightMouseUp || etype == NSOtherMouseUp) {
770     if (btn == 1) Fl::e_state &= ~FL_BUTTON1;
771     else if (btn == 3) Fl::e_state &= ~FL_BUTTON2;
772     else if (btn == 2) Fl::e_state &= ~FL_BUTTON3;
773     }
774     
775   switch ( etype ) {
776     case NSLeftMouseDown:
777     case NSRightMouseDown:
778     case NSOtherMouseDown:
779       suppressed = 0;
780       sendEvent = FL_PUSH;
781       Fl::e_is_click = 1; 
782       px = (int)pos.x; py = (int)pos.y;
783       if ([theEvent clickCount] > 1) 
784         Fl::e_clicks++;
785       else
786         Fl::e_clicks = 0;
787       // fall through
788     case NSLeftMouseUp:
789     case NSRightMouseUp:
790     case NSOtherMouseUp:
791       if (suppressed) {
792         suppressed = 0;
793         break;
794       }
795       if ( !window ) break;
796       if ( !sendEvent ) {
797         sendEvent = FL_RELEASE; 
798       }
799       Fl::e_keysym = keysym[ btn ];
800       // fall through
801     case NSMouseMoved:
802       suppressed = 0;
803       if ( !sendEvent ) { 
804         sendEvent = FL_MOVE; 
805       }
806       // fall through
807     case NSLeftMouseDragged:
808     case NSRightMouseDragged:
809     case NSOtherMouseDragged: {
810       if (suppressed) break;
811       if ( !sendEvent ) {
812         sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG
813         if (fabs(pos.x-px)>5 || fabs(pos.y-py)>5) 
814           Fl::e_is_click = 0;
815       }
816       mods_to_e_state( mods );
817       update_e_xy_and_e_xy_root([theEvent window]);
818       Fl::handle( sendEvent, window );
819       }
820       break;
821     default:
822       break;
823   }
824   
825   fl_unlock_function();
826   
827   return;
830 @interface FLTextView : NSTextView 
831 // this subclass is needed under OS X <= 10.5 but not under >= 10.6 where the base class is enough
834 @end
835 @implementation FLTextView
836 - (void)insertText:(id)aString
838   [[[NSApp keyWindow] contentView] insertText:aString];
840 - (void)doCommandBySelector:(SEL)aSelector
842   [[[NSApp keyWindow] contentView] doCommandBySelector:aSelector];
844 @end
847 Handle cocoa keyboard events
848 Events during a character composition sequence:
849  - keydown with deadkey -> [[theEvent characters] length] is 0
850  - keyup -> [theEvent characters] contains the deadkey
851  - keydown with next key -> [theEvent characters] contains the composed character
852  - keyup -> [theEvent characters] contains the standard character
853  */
854 static void cocoaKeyboardHandler(NSEvent *theEvent)
856   NSUInteger mods;
857   
858   // get the modifiers
859   mods = [theEvent modifierFlags];
860   // get the key code
861   UInt32 keyCode = 0, maskedKeyCode = 0;
862   unsigned short sym = 0;
863   keyCode = [theEvent keyCode];
864   NSString *s = [theEvent characters];  
865   if ( (mods & NSShiftKeyMask) && (mods & NSCommandKeyMask) ) {
866     s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit
867   }
868   // extended keyboards can also send sequences on key-up to generate Kanji etc. codes.
869   // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode.
870   // In this mode, there seem to be no key-down codes
871   // printf("%08x %08x %08x\n", keyCode, mods, key);
872   maskedKeyCode = keyCode & 0x7f;
874   if ([theEvent type] == NSKeyUp) {
875     Fl::e_state &= 0xbfffffff; // clear the deadkey flag
876   }
878   mods_to_e_state( mods ); // process modifier keys
879   sym = macKeyLookUp[maskedKeyCode];
880   if (sym < 0xff00) { // a "simple" key
881     // find the result of this key without modifier
882     NSString *sim = [theEvent charactersIgnoringModifiers];
883     UniChar one;
884     CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one);
885     // charactersIgnoringModifiers doesn't ignore shift, remove it when it's on
886     if(one >= 'A' && one <= 'Z') one += 32;
887     if (one > 0 && one <= 0x7f && (sym<'0' || sym>'9') ) sym = one;
888   }
889   Fl::e_keysym = Fl::e_original_keysym = sym;
891   //NSLog(@"cocoaKeyboardHandler: keycode=%08x keysym=%08x mods=%08x symbol=%@ (%@)",
892   //  keyCode, sym, mods, [theEvent characters], [theEvent charactersIgnoringModifiers]);
894   // If there is text associated with this key, it will be filled in later.
895   Fl::e_length = 0;
896   Fl::e_text = (char*)"";
901  * Open callback function to call...
902  */
904 static void     (*open_cb)(const char *) = 0;
908  * Install an open documents event handler...
909  */
910 @interface FLAppleEventHandler : NSObject
913 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent;
914 @end
915 @implementation FLAppleEventHandler
916 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent
918   NSAppleEventDescriptor *single = [event descriptorAtIndex:1];
919   const AEDesc *document = [single aeDesc];
920   long i, n;
921   FSRef fileRef;
922   AEKeyword keyWd;
923   DescType typeCd;
924   Size actSz;
925   char filename[1024];
926   // Lock access to FLTK in this thread...
927   fl_lock_function();
928   
929   // Open the documents via the callback...
930   if (AECountItems(document, &n) == noErr) {
931     for (i = 1; i <= n; i ++) {
932       AEGetNthPtr(document, i, typeFSRef, &keyWd, &typeCd,
933                   (Ptr)&fileRef, sizeof(fileRef),
934                   (actSz = sizeof(fileRef), &actSz));
935       FSRefMakePath( &fileRef, (UInt8*)filename, sizeof(filename) );
936       
937       (*open_cb)(filename);
938     }
939   }
940   // Unlock access to FLTK for all threads...
941   fl_unlock_function();
943 @end
945 void fl_open_callback(void (*cb)(const char *)) {
946   static NSAppleEventManager *aeventmgr = nil;
947   static FLAppleEventHandler *handler;
948   fl_open_display();
949   if (!aeventmgr) {
950     aeventmgr = [NSAppleEventManager sharedAppleEventManager];
951     handler = [[FLAppleEventHandler alloc] init];
952   }
953   
954   open_cb = cb;
955   if (cb) {
956     [aeventmgr setEventHandler:handler andSelector:\v@selector(handleAppleEvent:withReplyEvent:) 
957                  forEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
958   } else {
959     [aeventmgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments];  
960   }
965  * initialize the Mac toolboxes, dock status, and set the default menubar
966  */
968 extern "C" {
969   extern OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn, UInt32 _arg2,
970                                             UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
974 @interface FLDelegate : NSObject 
975 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
976 <NSWindowDelegate, NSApplicationDelegate>
977 #endif
980 - (void)windowDidMove:(NSNotification *)notif;
981 - (void)windowDidResize:(NSNotification *)notif;
982 - (void)windowDidResignKey:(NSNotification *)notif;
983 - (void)windowDidBecomeKey:(NSNotification *)notif;
984 - (void)windowDidBecomeMain:(NSNotification *)notif;
985 - (void)windowDidDeminiaturize:(NSNotification *)notif;
986 - (void)windowDidMiniaturize:(NSNotification *)notif;
987 - (void)windowWillClose:(NSNotification *)notif;
988 - (void)anywindowwillclosenotif:(NSNotification *)notif;
989 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
990 - (void)applicationDidBecomeActive:(NSNotification *)notify;
991 - (void)applicationWillResignActive:(NSNotification *)notify;
992 - (void)applicationWillHide:(NSNotification *)notify;
993 - (void)applicationWillUnhide:(NSNotification *)notify;
994 - (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client;
995 @end
996 @implementation FLDelegate
997 - (void)windowDidMove:(NSNotification *)notif
999   fl_lock_function();
1000   FLWindow *nsw = (FLWindow*)[notif object];
1001   Fl_Window *window = [nsw getFl_Window];
1002   NSPoint pt, pt2; 
1003   pt.x = 0;
1004   pt.y = [[nsw contentView] frame].size.height;
1005   pt2 = [nsw convertBaseToScreen:pt];
1006   update_e_xy_and_e_xy_root(nsw);
1007   window->position((int)pt2.x, (int)([[nsw screen] frame].size.height - pt2.y));
1008   if ([nsw containsGLsubwindow] ) {
1009     [nsw display];// redraw window after moving if it contains OpenGL subwindows
1010   }
1011   fl_unlock_function();
1013 - (void)windowDidResize:(NSNotification *)notif
1015   fl_lock_function();
1016   FLWindow *nsw = (FLWindow*)[notif object];
1017   Fl_Window *window = [nsw getFl_Window];
1018   NSRect r = [[nsw contentView] frame];
1019   NSPoint pt, pt2; 
1020   pt.x = 0;
1021   pt.y = [[nsw contentView] frame].size.height;
1022   pt2 = [nsw convertBaseToScreen:pt];
1023   resize_from_system = window;
1024   update_e_xy_and_e_xy_root(nsw);
1025   window->resize((int)pt2.x, 
1026                  (int)([[nsw screen] frame].size.height - pt2.y),
1027                  (int)r.size.width,
1028                  (int)r.size.height);
1029   fl_unlock_function();
1031 - (void)windowDidResignKey:(NSNotification *)notif
1033   fl_lock_function();
1034   FLWindow *nsw = (FLWindow*)[notif object];
1035   Fl_Window *window = [nsw getFl_Window];
1036   Fl::handle( FL_UNFOCUS, window);
1037   fl_unlock_function();
1039 - (void)windowDidBecomeKey:(NSNotification *)notif
1041   fl_lock_function();
1042   FLWindow *nsw = (FLWindow*)[notif object];
1043   Fl_Window *w = [nsw getFl_Window];
1044   if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w);
1045   fl_unlock_function();
1047 - (void)windowDidBecomeMain:(NSNotification *)notif
1049   fl_lock_function();
1050   FLWindow *nsw = (FLWindow*)[notif object];
1051   Fl_Window *window = [nsw getFl_Window];
1052   Fl::first_window(window);
1053   update_e_xy_and_e_xy_root(nsw);
1054   fl_unlock_function();
1056 - (void)windowDidDeminiaturize:(NSNotification *)notif
1058   fl_lock_function();
1059   FLWindow *nsw = (FLWindow*)[notif object];
1060   Fl_Window *window = [nsw getFl_Window];
1061   Fl::handle(FL_SHOW, window);
1062   update_e_xy_and_e_xy_root(nsw);
1063   fl_unlock_function();
1065 - (void)windowDidMiniaturize:(NSNotification *)notif
1067   fl_lock_function();
1068   FLWindow *nsw = (FLWindow*)[notif object];
1069   Fl_Window *window = [nsw getFl_Window];
1070   Fl::handle(FL_HIDE, window);
1071   fl_unlock_function();
1073 - (void)windowWillClose:(NSNotification *)notif
1075   fl_lock_function();
1076   Fl_Window *w = Fl::first_window();
1077   if (w) {
1078     NSWindow *cw = (NSWindow*)Fl_X::i(w)->xid;
1079     if ( ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) {
1080       if (![cw isKeyWindow]) {  // always make Fl::first_window() the key widow
1081         [cw makeKeyAndOrderFront:nil];
1082       }
1083       if (![cw isMainWindow]) { // always make Fl::first_window() the main widow
1084         [cw makeMainWindow];
1085       }
1086     }
1087   }
1088   fl_unlock_function();
1090 - (void)anywindowwillclosenotif:(NSNotification *)notif
1092   // necessary so that after closing a non-FLTK window (e.g., Fl_Native_File_Chooser)
1093   // the front window turns key again
1094   NSWindow *closing = (NSWindow*)[notif object];
1095   if ([closing isMemberOfClass:[FLWindow class]]) return;
1096   NSWindow *nsk = [NSApp keyWindow];
1097   NSWindow *nsm = [NSApp mainWindow];
1098   if ([nsm isMemberOfClass:[FLWindow class]] && nsk == nil) {
1099     [nsm makeKeyAndOrderFront:nil];
1100   }
1102 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
1104   fl_lock_function();
1105   NSApplicationTerminateReply reply = NSTerminateNow;
1106   while ( Fl_X::first ) {
1107     Fl_X *x = Fl_X::first;
1108     Fl::handle( FL_CLOSE, x->w );
1109     if ( Fl_X::first == x ) {
1110       reply = NSTerminateCancel; // FLTK has not closed all windows, so we return to the main program now
1111       break;
1112     }
1113   }
1114   fl_unlock_function();
1115   return reply;
1118  * Cocoa organizes the Z depth of windows on a global priority. FLTK however
1119  * expects the window manager to organize Z level by application. The trickery
1120  * below will change Z order during activation and deactivation.
1121  */
1122 - (void)applicationDidBecomeActive:(NSNotification *)notify
1124   fl_lock_function();
1125   Fl_X *x;
1126   FLWindow *top = 0, *topModal = 0, *topNonModal = 0;
1127   for (x = Fl_X::first;x;x = x->next) {
1128     FLWindow *cw = (FLWindow*)x->xid;
1129     Fl_Window *win = x->w;
1130     if (win && cw) {
1131       if (win->modal()) {
1132         [cw setLevel:NSModalPanelWindowLevel];
1133         if (topModal) 
1134           [cw orderWindow:NSWindowBelow relativeTo:[topModal windowNumber]];
1135         else
1136           topModal = cw;
1137       } else if (win->non_modal()) {
1138         [cw setLevel:NSFloatingWindowLevel];
1139         if (topNonModal) 
1140           [cw orderWindow:NSWindowBelow relativeTo:[topNonModal windowNumber]];
1141         else
1142           topNonModal = cw;
1143       } else {
1144         if (top) 
1145           ;
1146         else
1147           top = cw;
1148       }
1149     }
1150   }
1151   fl_unlock_function();
1153 - (void)applicationWillResignActive:(NSNotification *)notify
1155   fl_lock_function();
1156   Fl_X *x;
1157   FLWindow *top = 0;
1158   // sort in all regular windows
1159   for (x = Fl_X::first;x;x = x->next) {
1160     FLWindow *cw = (FLWindow*)x->xid;
1161     Fl_Window *win = x->w;
1162     if (win && cw) {
1163       if (win->modal()) {
1164       } else if (win->non_modal()) {
1165       } else {
1166         if (!top) top = cw;
1167       }
1168     }
1169   }
1170   // now sort in all modals
1171   for (x = Fl_X::first;x;x = x->next) {
1172     FLWindow *cw = (FLWindow*)x->xid;
1173     Fl_Window *win = x->w;
1174     if (win && cw) {
1175       if (win->modal()) {
1176         [cw setLevel:NSNormalWindowLevel];
1177         if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]];
1178       }
1179     }
1180   }
1181   // finally all non-modals
1182   for (x = Fl_X::first;x;x = x->next) {
1183     FLWindow *cw = (FLWindow*)x->xid;
1184     Fl_Window *win = x->w;
1185     if (win && cw) {
1186       if (win->non_modal()) {
1187         [cw setLevel:NSNormalWindowLevel];
1188         if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]];
1189       }
1190     }
1191   }
1192   fl_unlock_function();
1194 - (void)applicationWillHide:(NSNotification *)notify
1196   fl_lock_function();
1197   Fl_X *x;
1198   for (x = Fl_X::first;x;x = x->next) {
1199     Fl_Window *window = x->w;
1200     if ( !window->parent() ) Fl::handle( FL_HIDE, window);
1201     }
1202   fl_unlock_function();
1204 - (void)applicationWillUnhide:(NSNotification *)notify
1206   fl_lock_function();
1207   Fl_X *x;
1208   for (x = Fl_X::first;x;x = x->next) {
1209     Fl_Window *w = x->w;
1210     if ( !w->parent() ) {
1211       if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w);
1212       Fl::handle( FL_SHOW, w);
1213       }
1214   }
1215   fl_unlock_function();
1217 - (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
1219   if (fl_mac_os_version < 100600) {
1220     static FLTextView *view = nil;
1221     if (!view) {
1222       NSRect rect={{0,0},{20,20}};
1223       view = [[FLTextView alloc] initWithFrame:rect];
1224     }
1225     return view;
1226   }
1227   return nil;
1229 @end
1231 @implementation FLApplication
1232 + (void)sendEvent:(NSEvent *)theEvent
1234   NSEventType type = [theEvent type];  
1235   if (type == NSLeftMouseDown) {
1236     fl_lock_function();
1237     Fl_Window *grab = Fl::grab();
1238     if (grab) {
1239       FLWindow *win = (FLWindow *)[theEvent window];
1240       if ( [win isKindOfClass:[FLWindow class]] && grab != [win getFl_Window]) {
1241         // a click event out of a menu window, so we should close this menu
1242         // done here to catch also clicks on window title bar/resize box 
1243         cocoaMouseHandler(theEvent);
1244       }
1245     }
1246     fl_unlock_function();
1247   } else if (type == NSApplicationDefined) {
1248     if ([theEvent subtype] == FLTKDataReadyEvent) {
1249       processFLTKEvent();
1250     }
1251     return;
1252   } else if (type == NSKeyUp) {
1253     // The default sendEvent turns key downs into performKeyEquivalent when
1254     // modifiers are down, but swallows the key up if the modifiers include
1255     // command.  This one makes all modifiers consistent by always sending key ups.
1256     // FLView treats performKeyEquivalent to keyDown, but performKeyEquivalent is
1257     // still needed for the system menu.
1258     [[NSApp keyWindow] sendEvent:theEvent];
1259     return;
1260     }
1261   [NSApp sendEvent:theEvent]; 
1263 @end
1265 static FLDelegate *mydelegate;
1267 void fl_open_display() {
1268   static char beenHereDoneThat = 0;
1269   if ( !beenHereDoneThat ) {
1270     beenHereDoneThat = 1;
1272     BOOL need_new_nsapp = (NSApp == nil);
1273     if (need_new_nsapp) [NSApplication sharedApplication];
1274     NSAutoreleasePool *localPool;
1275     localPool = [[NSAutoreleasePool alloc] init]; // never released
1276     mydelegate = [[FLDelegate alloc] init];
1277     [NSApp setDelegate:mydelegate];
1278     if (need_new_nsapp) [NSApp finishLaunching];
1280     // empty the event queue but keep system events for drag&drop of files at launch
1281     NSEvent *ign_event;
1282     do ign_event = [NSApp nextEventMatchingMask:(NSAnyEventMask & ~NSSystemDefinedMask)
1283                                         untilDate:[NSDate dateWithTimeIntervalSinceNow:0] 
1284                                            inMode:NSDefaultRunLoopMode 
1285                                           dequeue:YES];
1286     while (ign_event);
1287     
1288     fl_default_cursor = [NSCursor arrowCursor];
1290     // bring the application into foreground without a 'CARB' resource
1291     Boolean same_psn;
1292     ProcessSerialNumber cur_psn, front_psn;
1293     if ( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) &&
1294          !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn ) {
1295       // only transform the application type for unbundled apps
1296       CFBundleRef bundle = CFBundleGetMainBundle();
1297       if ( bundle ) {
1298         FSRef execFs;
1299         CFURLRef execUrl = CFBundleCopyExecutableURL( bundle );
1300         CFURLGetFSRef( execUrl, &execFs );
1301         
1302         FSRef bundleFs;
1303         GetProcessBundleLocation( &cur_psn, &bundleFs );
1304         
1305         if ( !FSCompareFSRefs( &execFs, &bundleFs ) )
1306           bundle = NULL;
1307         
1308         CFRelease(execUrl);
1309       }
1310             
1311       if ( !bundle )
1312       {
1313         // Earlier versions of this code tried to use weak linking, however it
1314         // appears that this does not work on 10.2.  Since 10.3 and higher provide
1315         // both TransformProcessType and CPSEnableForegroundOperation, the following
1316         // conditional code compiled on 10.2 will still work on newer releases...
1317         OSErr err;
1318 #if __LP64__
1319         err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication);
1320 #else
1321         
1322 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1323         if (TransformProcessType != NULL) {
1324           err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication);
1325         } else
1326 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1327           err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103);
1328 #endif // __LP64__
1329         if (err == noErr) {
1330           SetFrontProcess( &cur_psn );
1331         }
1332       }
1333     }
1334     if (![NSApp servicesMenu]) createAppleMenu();
1335     fl_system_menu = [NSApp mainMenu];
1336     
1337     [[NSNotificationCenter defaultCenter] addObserver:mydelegate 
1338                selector:@selector(anywindowwillclosenotif:) 
1339                    name:NSWindowWillCloseNotification 
1340                  object:nil];
1341   }
1346  * get rid of allocated resources
1347  */
1348 void fl_close_display() {
1352 // Gets the border sizes and the titlebar size
1353 static void get_window_frame_sizes(int &bx, int &by, int &bt) {
1354   static bool first = true;
1355   static int top, left, bottom;
1356   if (first) {
1357     first = false;
1358     if (NSApp == nil) fl_open_display();
1359     NSRect inside = { {20,20}, {100,100} };
1360     NSRect outside = [NSWindow  frameRectForContentRect:inside styleMask:NSTitledWindowMask];
1361     left = int(outside.origin.x - inside.origin.x);
1362     bottom = int(outside.origin.y - inside.origin.y);
1363     top = int(outside.size.height - inside.size.height) - bottom;
1364     }
1365   bx = left;
1366   by = bottom;
1367   bt = top;
1371  * smallest x ccordinate in screen space
1372  */
1373 int Fl::x() {
1374   return int([[NSScreen mainScreen] visibleFrame].origin.x);
1379  * smallest y coordinate in screen space
1380  */
1381 int Fl::y() {
1382   NSRect all = [[NSScreen mainScreen] frame];
1383   NSRect visible = [[NSScreen mainScreen] visibleFrame];
1384   return int(all.size.height - (visible.origin.y + visible.size.height));
1389  * screen width
1390  */
1391 int Fl::w() {
1392   return int([[NSScreen mainScreen] visibleFrame].size.width);
1397  * screen height
1398  */
1399 int Fl::h() {
1400   return int([[NSScreen mainScreen] visibleFrame].size.height);
1405  * get the current mouse pointer world coordinates
1406  */
1407 void Fl::get_mouse(int &x, int &y) 
1409   fl_open_display();
1410   NSPoint pt = [NSEvent mouseLocation];
1411   x = int(pt.x);
1412   y = int([[NSScreen mainScreen] frame].size.height - pt.y);
1417  * Initialize the given port for redraw and call the window's flush() to actually draw the content
1418  */ 
1419 void Fl_X::flush()
1421   w->flush();
1422   if (fl_gc) CGContextFlush(fl_gc);
1426  * Gets called when a window is created, resized, or deminiaturized
1427  */    
1428 static void handleUpdateEvent( Fl_Window *window ) 
1430   if ( !window ) return;
1431   Fl_X *i = Fl_X::i( window );
1432   i->wait_for_expose = 0;
1434   if ( i->region ) {
1435     XDestroyRegion(i->region);
1436     i->region = 0;
1437   }
1438   
1439   for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) {
1440     if ( cx->region ) {
1441       XDestroyRegion(cx->region);
1442       cx->region = 0;
1443     }
1444     cx->w->clear_damage(FL_DAMAGE_ALL);
1445     cx->flush();
1446     cx->w->clear_damage();
1447   }
1448   window->clear_damage(FL_DAMAGE_ALL);
1449   i->flush();
1450   window->clear_damage();
1451 }     
1454 int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) {
1455   int W, H, xoff, yoff, dx, dy;
1456   int ret = bx = by = bt = 0;
1457   if (w->border() && !w->parent()) {
1458     if (w->maxw != w->minw || w->maxh != w->minh) {
1459       ret = 2;
1460     } else {
1461       ret = 1;
1462     }
1463     get_window_frame_sizes(bx, by, bt);
1464   }
1465   // The coordinates of the whole window, including non-client area
1466   xoff = bx;
1467   yoff = by + bt;
1468   dx = 2*bx;
1469   dy = 2*by + bt;
1470   X = w->x()-xoff;
1471   Y = w->y()-yoff;
1472   W = w->w()+dx;
1473   H = w->h()+dy;
1474   
1475   // Proceed to positioning the window fully inside the screen, if possible
1476   
1477   // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk
1478   // that we want to avoid when positioning our window, namely the Dock and the
1479   // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the
1480   // list of all available screens and find the one that this window is most
1481   // likely to go to, and then reposition it to fit withing the 'good' area.
1482   //  Rect r;
1483   // find the screen, that the center of this window will fall into
1484   int R = X+W, B = Y+H; // right and bottom
1485   int cx = (X+R)/2, cy = (Y+B)/2; // center of window;
1486   NSScreen *gd = NULL;
1487   NSArray *a = [NSScreen screens]; int count = (int)[a count]; NSRect r; int i;
1488   for( i = 0; i < count; i++) {
1489     r = [[a objectAtIndex:i] frame];
1490     cy = int(r.size.height - cy);
1491     if (   cx >= r.origin.x && cx <= r.origin.x + r.size.width
1492         && cy >= r.origin.y && cy <= r.origin.y + r.size.height)
1493       break;
1494   }
1495   if (i < count) gd = [a objectAtIndex:i];
1496   
1497   // if the center doesn't fall on a screen, try the top left
1498   if (!gd) {
1499     for( i = 0; i < count; i++) {
1500       r = [[a objectAtIndex:i] frame];
1501       if (    X >= r.origin.x && X <= r.origin.x + r.size.width
1502           && r.size.height - Y >= r.origin.y  && r.size.height - Y <= r.origin.y + r.size.height)
1503         break;
1504     }
1505     if (i < count) gd = [a objectAtIndex:i];
1506   }
1507   // if that doesn't fall on a screen, try the top right
1508   if (!gd) {
1509     for( i = 0; i < count; i++) {
1510       r = [[a objectAtIndex:i] frame];
1511       if (    R >= r.origin.x && R <= r.origin.x + r.size.width
1512           && r.size.height - Y >= r.origin.y  && r.size.height - Y <= r.origin.y + r.size.height)
1513         break;
1514     }
1515     if (i < count) gd = [a objectAtIndex:i];
1516   }
1517   // if that doesn't fall on a screen, try the bottom left
1518   if (!gd) {
1519     for( i = 0; i < count; i++) {
1520       r = [[a objectAtIndex:i] frame];
1521       if (    X >= r.origin.x && X <= r.origin.x + r.size.width
1522           && Y-H >= r.origin.y  && Y-H <= r.origin.y + r.size.height)
1523         break;
1524     }
1525     if (i < count) gd = [a objectAtIndex:i];
1526   }
1527   // last resort, try the bottom right
1528   if (!gd) {
1529     for( i = 0; i < count; i++) {
1530       r = [[a objectAtIndex:i] frame];
1531       if (    R >= r.origin.x && R <= r.origin.x + r.size.width
1532           && Y-H >= r.origin.y  && Y-H <= r.origin.y + r.size.height)
1533         break;
1534     }
1535     if (i < count) gd = [a objectAtIndex:i];
1536   }
1537   // if we still have not found a screen, we will use the main
1538   // screen, the one that has the application menu bar.
1539   if (!gd) gd = [a objectAtIndex:0];
1540   if (gd) {
1541     r = [gd visibleFrame];
1542     int sh = int([gd frame].size.height);
1543     if ( R > r.origin.x + r.size.width ) X -= int(R - (r.origin.x + r.size.width));
1544     if ( B > sh - r.origin.y ) Y -= int(B - (sh - r.origin.y));
1545     if ( X < r.origin.x ) X = int(r.origin.x);
1546     if ( Y < sh - (r.origin.y + r.size.height) ) Y = int(sh - (r.origin.y + r.size.height));
1547   }
1548   
1549   // Return the client area's top left corner in (X,Y)
1550   X+=xoff;
1551   Y+=yoff;
1552   
1553   return ret;
1557 Fl_Window *fl_dnd_target_window = 0;
1559 static void  q_set_window_title(NSWindow *nsw, const char * name, const char *mininame) {
1560   CFStringRef title = CFStringCreateWithCString(NULL, (name ? name : ""), kCFStringEncodingUTF8);
1561   if(!title) { // fallback when name contains malformed UTF-8
1562     int l = strlen(name);
1563     unsigned short* utf16 = new unsigned short[l + 1];
1564     l = fl_utf8toUtf16(name, l, utf16, l + 1);
1565     title = CFStringCreateWithCharacters(NULL, utf16, l);
1566     delete[] utf16;
1567     }
1568   [nsw setTitle:(NSString*)title];
1569   CFRelease(title);
1570   if (mininame && strlen(mininame)) {
1571     CFStringRef minititle = CFStringCreateWithCString(NULL, mininame, kCFStringEncodingUTF8);
1572     if (minititle) {
1573       [nsw setMiniwindowTitle:(NSString*)minititle];
1574       CFRelease(minititle);
1575     }
1576   }
1580 @interface FLView : NSView <NSTextInput> {
1581   int next_compose_length;
1582   bool in_key_event;
1584 + (void)prepareEtext:(NSString*)aString;
1585 - (id)init;
1586 - (void)drawRect:(NSRect)rect;
1587 - (BOOL)acceptsFirstResponder;
1588 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent;
1589 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent;
1590 - (void)mouseUp:(NSEvent *)theEvent;
1591 - (void)rightMouseUp:(NSEvent *)theEvent;
1592 - (void)otherMouseUp:(NSEvent *)theEvent;
1593 - (void)mouseDown:(NSEvent *)theEvent;
1594 - (void)rightMouseDown:(NSEvent *)theEvent;
1595 - (void)otherMouseDown:(NSEvent *)theEvent;
1596 - (void)mouseMoved:(NSEvent *)theEvent;
1597 - (void)mouseDragged:(NSEvent *)theEvent;
1598 - (void)rightMouseDragged:(NSEvent *)theEvent;
1599 - (void)otherMouseDragged:(NSEvent *)theEvent;
1600 - (void)scrollWheel:(NSEvent *)theEvent;
1601 - (BOOL)handleKeyDown:(NSEvent *)theEvent;
1602 - (void)keyDown:(NSEvent *)theEvent;
1603 - (void)keyUp:(NSEvent *)theEvent;
1604 - (void)flagsChanged:(NSEvent *)theEvent;
1605 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender;
1606 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender;
1607 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
1608 - (void)draggingExited:(id < NSDraggingInfo >)sender;
1609 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
1610 @end
1612 @implementation FLView
1613 - (id)init
1615   self = [super init];
1616   if (self) {
1617     next_compose_length = -1;
1618     in_key_event = false;
1619     }
1620   return self;
1622 - (void)drawRect:(NSRect)rect
1624   fl_lock_function();
1625   FLWindow *cw = (FLWindow*)[self window];
1626   Fl_Window *w = [cw getFl_Window];
1627   handleUpdateEvent(w);
1628   fl_unlock_function();
1631 - (BOOL)acceptsFirstResponder
1632 {   
1633   return YES;
1635 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
1636 {   
1637   //NSLog(@"performKeyEquivalent:");
1638   return [self handleKeyDown:theEvent];
1640 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent
1641 {   
1642   Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window];
1643   Fl_Window *first = Fl::first_window();
1644   return (first == w || !first->modal());
1646 - (void)mouseUp:(NSEvent *)theEvent {
1647   cocoaMouseHandler(theEvent);
1649 - (void)rightMouseUp:(NSEvent *)theEvent {
1650   cocoaMouseHandler(theEvent);
1652 - (void)otherMouseUp:(NSEvent *)theEvent {
1653   cocoaMouseHandler(theEvent);
1655 - (void)mouseDown:(NSEvent *)theEvent {
1656   cocoaMouseHandler(theEvent);
1658 - (void)rightMouseDown:(NSEvent *)theEvent {
1659   cocoaMouseHandler(theEvent);
1661 - (void)otherMouseDown:(NSEvent *)theEvent {
1662   cocoaMouseHandler(theEvent);
1664 - (void)mouseMoved:(NSEvent *)theEvent {
1665   cocoaMouseHandler(theEvent);
1667 - (void)mouseDragged:(NSEvent *)theEvent {
1668   cocoaMouseHandler(theEvent);
1670 - (void)rightMouseDragged:(NSEvent *)theEvent {
1671   cocoaMouseHandler(theEvent);
1673 - (void)otherMouseDragged:(NSEvent *)theEvent {
1674   cocoaMouseHandler(theEvent);
1676 - (void)scrollWheel:(NSEvent *)theEvent {
1677   cocoaMouseWheelHandler(theEvent);
1679 - (BOOL)handleKeyDown:(NSEvent *)theEvent {
1680   //NSLog(@"handleKeyDown");
1681   fl_lock_function();
1683   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
1684   Fl::first_window(window);
1686   next_compose_length = -1;
1687   // First let's process the raw key press
1688   cocoaKeyboardHandler(theEvent);
1690   int no_text_key = false;
1691   static const int notext[] = { // keys that don't emit text
1692     FL_BackSpace, FL_Print, FL_Scroll_Lock, FL_Pause,
1693     FL_Insert, FL_Home, FL_Page_Up, FL_Delete, FL_End, FL_Page_Down,
1694     FL_Left, FL_Up, FL_Right, FL_Down, 
1695     FL_Menu, FL_Num_Lock, FL_Help 
1696   };
1697   static const int count = sizeof(notext)/sizeof(int);
1698   if (Fl::e_keysym > FL_F && Fl::e_keysym <= FL_F_Last) no_text_key = true;
1699   else for (int i=0; i < count; i++) {
1700     if (notext[i] == Fl::e_keysym) {
1701       no_text_key = true;
1702       break;
1703     }
1704   }
1705   if (!no_text_key && !(Fl::e_state & FL_META) ) {
1706     // Don't send cmd-<key> to interpretKeyEvents because it beeps.
1707     // Then we can let the OS have a stab at it and see if it thinks it
1708     // should result in some text
1709     NSText *edit = [[theEvent window]  fieldEditor:YES forObject:nil];
1710     in_key_event = true;
1711     [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
1712     in_key_event = false;
1713   }
1714   //NSLog(@"to text=%@ l=%d", [NSString stringWithUTF8String:Fl::e_text], Fl::e_length);
1715   int handled = Fl::handle(FL_KEYDOWN, window);
1716   // We have to update this after Fl::handle as it says what to do on the
1717   // _next_ input
1718   if (next_compose_length != -1)
1719     Fl::compose_state = next_compose_length;
1721   fl_unlock_function();
1722   return (handled ? YES : NO);
1724 - (void)keyDown:(NSEvent *)theEvent {
1725   //NSLog(@"keyDown: ");
1726   [self handleKeyDown:theEvent];
1728 - (void)keyUp:(NSEvent *)theEvent {
1729   //NSLog(@"keyUp: ");
1730   fl_lock_function();
1731   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
1732   Fl::first_window(window);
1733   cocoaKeyboardHandler(theEvent);
1734   NSString *s = [theEvent characters];
1735   if ([s length] >= 1) [FLView prepareEtext:[s substringToIndex:1]];
1736   Fl::handle(FL_KEYUP,window);
1737   fl_unlock_function();
1739 - (void)flagsChanged:(NSEvent *)theEvent {
1740   //NSLog(@"flagsChanged: ");
1741   fl_lock_function();
1742   static UInt32 prevMods = 0;
1743   NSUInteger mods = [theEvent modifierFlags];
1744   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
1745   UInt32 tMods = prevMods ^ mods;
1746   int sendEvent = 0;
1747   if ( tMods )
1748   {
1749     unsigned short keycode = [theEvent keyCode];
1750     Fl::e_keysym = Fl::e_original_keysym = macKeyLookUp[keycode & 0x7f];
1751     if ( Fl::e_keysym ) 
1752       sendEvent = ( prevMods<mods ) ? FL_KEYBOARD : FL_KEYUP;
1753     Fl::e_length = 0;
1754     Fl::e_text = (char*)"";
1755     prevMods = mods;
1756   }
1757   mods_to_e_state( mods );
1758   while (window->parent()) window = window->window();
1759   if (sendEvent) Fl::handle(sendEvent,window);
1760   fl_unlock_function();
1762 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
1764   fl_lock_function();
1765   Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
1766   update_e_xy_and_e_xy_root([self window]);
1767   fl_dnd_target_window = target;
1768   int ret = Fl::handle( FL_DND_ENTER, target );
1769   breakMacEventLoop();
1770   fl_unlock_function();
1771   return ret ? NSDragOperationCopy : NSDragOperationNone;
1773 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
1775   fl_lock_function();
1776   Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
1777   update_e_xy_and_e_xy_root([self window]);
1778   fl_dnd_target_window = target;
1779   int ret = Fl::handle( FL_DND_DRAG, target );
1780   breakMacEventLoop();
1781   fl_unlock_function();
1782   return ret ? NSDragOperationCopy : NSDragOperationNone;
1784 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender 
1786   static char *DragData = NULL;
1787   fl_lock_function();
1788   Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
1789   if ( !Fl::handle( FL_DND_RELEASE, target ) ) { 
1790     breakMacEventLoop();
1791     fl_unlock_function();
1792     return NO;
1793   }
1794   NSPasteboard *pboard;
1795   // NSDragOperation sourceDragMask;
1796   // sourceDragMask = [sender draggingSourceOperationMask];
1797   pboard = [sender draggingPasteboard];
1798   update_e_xy_and_e_xy_root([self window]);
1799   if (DragData) { free(DragData); DragData = NULL; }
1800   if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {
1801     CFArrayRef files = (CFArrayRef)[pboard propertyListForType:NSFilenamesPboardType];
1802     CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n"));
1803     int l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), kCFStringEncodingUTF8);
1804     DragData = (char *)malloc(l + 1);
1805     CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8);
1806     CFRelease(all);
1807   }
1808   else if ( [[pboard types] containsObject:NSStringPboardType] ) {
1809     NSData *data = [pboard dataForType:NSStringPboardType];
1810     DragData = (char *)malloc([data length] + 1);
1811     [data getBytes:DragData];
1812     DragData[[data length]] = 0;
1813     convert_crlf(DragData, strlen(DragData));
1814   }
1815   else {
1816     breakMacEventLoop();
1817     fl_unlock_function();
1818     return NO;
1819   }
1820   Fl::e_text = DragData;
1821   Fl::e_length = strlen(DragData);
1822   int old_event = Fl::e_number;
1823   Fl::belowmouse()->handle(Fl::e_number = FL_PASTE);
1824   Fl::e_number = old_event;
1825   if (DragData) { free(DragData); DragData = NULL; }
1826   Fl::e_text = NULL;
1827   Fl::e_length = 0;
1828   fl_dnd_target_window = NULL;
1829   breakMacEventLoop();
1830   fl_unlock_function();
1831   return YES;
1833 - (void)draggingExited:(id < NSDraggingInfo >)sender
1835   fl_lock_function();
1836   if ( fl_dnd_target_window ) {
1837     Fl::handle( FL_DND_LEAVE, fl_dnd_target_window );
1838     fl_dnd_target_window = 0;
1839   }
1840   fl_unlock_function();
1842 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
1844   return NSDragOperationGeneric;
1847 + (void)prepareEtext:(NSString*)aString {
1848   // fills Fl::e_text with UTF-8 encoded aString using an adequate memory allocation
1849   static char *received_utf8 = NULL;
1850   static int lreceived = 0;
1851   char *p = (char*)[aString UTF8String];
1852   int l = strlen(p);
1853   if (l > 0) {
1854     if (lreceived == 0) {
1855       received_utf8 = (char*)malloc(l + 1);
1856       lreceived = l;
1857     }
1858     else if (l > lreceived) {
1859       received_utf8 = (char*)realloc(received_utf8, l + 1);
1860       lreceived = l;
1861     }
1862     strcpy(received_utf8, p);
1863     Fl::e_text = received_utf8;
1864   }
1865   Fl::e_length = l;
1868 // These functions implement text input.
1869 // Only two-stroke character composition works at this point.
1870 // Needs much elaboration to fully support CJK text input,
1871 // but this is the way to go.
1872 - (void)doCommandBySelector:(SEL)aSelector {
1875 - (void)insertText:(id)aString {
1876   NSString *received;
1877   if ([aString isKindOfClass:[NSAttributedString class]]) {
1878     received = [(NSAttributedString*)aString string];
1879   } else {
1880     received = (NSString*)aString;
1881   }
1882   //NSLog(@"insertText: received=%@",received);
1884   if (!in_key_event) fl_lock_function();
1885   [FLView prepareEtext:received];
1886   // We can get called outside of key events (e.g. from the character
1887   // palette). Transform such actions to FL_PASTE events.
1888   if (!in_key_event) {
1889     Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
1890     Fl::handle(FL_PASTE, target);
1891     // for some reason, the window does not redraw until the next mouse move or button push
1892     // sending a 'redraw()' or 'awake()' does not solve the issue!
1893     Fl::flush();
1894   }
1895   if (!in_key_event) fl_unlock_function();
1898 - (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection  {
1899   NSString *received;
1900   if (newSelection.location == 0) {
1901     [self unmarkText];
1902     return;
1903   }
1904   if ([aString isKindOfClass:[NSAttributedString class]]) {
1905     received = [(NSAttributedString*)aString string];
1906   } else {
1907     received = (NSString*)aString;
1908   }
1909   //NSLog(@"setMarkedText: %@ %d %d",received,newSelection.location,newSelection.length);
1910   // This code creates the OS X behaviour of seeing dead keys as things
1911   // are being composed.
1912   next_compose_length = newSelection.location;
1913   [FLView prepareEtext:received];
1914   //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", received, Fl::e_length, next_compose_length);
1917 - (void)unmarkText {
1918   fl_lock_function();
1919   Fl::compose_state = 0;
1920   fl_unlock_function();
1921   //NSLog(@"unmarkText");
1924 - (NSRange)selectedRange {
1925   return NSMakeRange(NSNotFound, 0);
1928 - (NSRange)markedRange {
1929   //NSLog(@"markedRange ?");
1930   return NSMakeRange(NSNotFound, Fl::compose_state);
1933 - (BOOL)hasMarkedText {
1934   //NSLog(@"hasMarkedText %s", Fl::compose_state > 0?"YES":"NO");
1935   return (Fl::compose_state > 0);
1938 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
1939   //NSLog(@"attributedSubstringFromRange: %d %d",aRange.location,aRange.length);
1940   return nil;
1943 - (NSArray *)validAttributesForMarkedText {
1944   return nil;
1947 - (NSRect)firstRectForCharacterRange:(NSRange)aRange {
1948   NSRect glyphRect;
1949   fl_lock_function();
1950   Fl_Widget *focus = Fl::focus();
1951   Fl_Window *wfocus = focus->window();
1952   while (wfocus->window()) wfocus = wfocus->window();
1953   glyphRect.size.width = 0;
1954   
1955   if (dynamic_cast<Fl_Text_Display*>(focus) != NULL) {
1956     int x, y;
1957     Fl_Text_Display *current = (Fl_Text_Display*)focus;
1958     current->position_to_xy( current->insert_position(), &x, &y );
1959     glyphRect.origin.x = (CGFloat)x;
1960     glyphRect.origin.y = (CGFloat)y + current->textsize();
1961     glyphRect.size.height = current->textsize();
1962   } else {
1963     glyphRect.origin.x = focus->x();
1964     glyphRect.origin.y = focus->y() + focus->h();
1965     glyphRect.size.height = 12;
1966   }
1967   // Convert the rect to screen coordinates
1968   glyphRect.origin.y = wfocus->h() - glyphRect.origin.y;
1969   glyphRect.origin = [[self window] convertBaseToScreen:glyphRect.origin];
1970   fl_unlock_function();
1971   return glyphRect;
1974 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
1975   return 0;
1978 - (NSInteger)conversationIdentifier {
1979   return (NSInteger)self;
1982 @end
1986  * go ahead, create that (sub)window
1987  */
1988 void Fl_X::make(Fl_Window* w)
1990   static int xyPos = 100;
1991   if ( w->parent() ) {          // create a subwindow
1992     Fl_Group::current(0);
1993     // our subwindow needs this structure to know about its clipping. 
1994     Fl_X* x = new Fl_X;
1995     x->subwindow = true;
1996     x->other_xid = 0;
1997     x->region = 0;
1998     x->subRegion = 0;
1999     x->cursor = fl_default_cursor;
2000     x->gc = 0;                  // stay 0 for Quickdraw; fill with CGContext for Quartz
2001     Fl_Window *win = w->window();
2002     Fl_X *xo = Fl_X::i(win);
2003     if (xo) {
2004       x->xidNext = xo->xidChildren;
2005       x->xidChildren = 0L;
2006       xo->xidChildren = x;
2007       x->xid = win->i->xid;
2008       x->w = w; w->i = x;
2009       x->wait_for_expose = 0;
2010       {
2011         Fl_X *z = xo->next;     // we don't want a subwindow in Fl_X::first
2012         xo->next = x;
2013         x->next = z;
2014       }
2015       int old_event = Fl::e_number;
2016       w->handle(Fl::e_number = FL_SHOW);
2017       Fl::e_number = old_event;
2018       w->redraw();              // force draw to happen
2019     }
2020     if (w->as_gl_window()) { // if creating a sub-GL-window
2021       while (win->window()) win = win->window();
2022       [(FLWindow*)Fl_X::i(win)->xid setContainsGLsubwindow:YES];
2023     }
2024     fl_show_iconic = 0;
2025   }
2026   else {                        // create a desktop window
2027     Fl_Group::current(0);
2028     fl_open_display();
2029     NSInteger winlevel = NSNormalWindowLevel;
2030     NSUInteger winstyle;
2031     if (w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
2032     else winstyle = NSBorderlessWindowMask;
2033     int xp = w->x();
2034     int yp = w->y();
2035     int wp = w->w();
2036     int hp = w->h();
2037     if (w->size_range_set) {
2038       if ( w->minh != w->maxh || w->minw != w->maxw) {
2039         winstyle |= NSResizableWindowMask;
2040       }
2041     } else {
2042       if (w->resizable()) {
2043         Fl_Widget *o = w->resizable();
2044         int minw = o->w(); if (minw > 100) minw = 100;
2045         int minh = o->h(); if (minh > 100) minh = 100;
2046         w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
2047         winstyle |= NSResizableWindowMask;
2048       } else {
2049         w->size_range(w->w(), w->h(), w->w(), w->h());
2050       }
2051     }
2052     int xwm = xp, ywm = yp, bt, bx, by;
2053     
2054     if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) {
2055       // menu windows and tooltips
2056       if (w->modal()||w->tooltip_window()) {
2057         winstyle = NSBorderlessWindowMask;
2058         winlevel = NSModalPanelWindowLevel;
2059       } else {
2060         winstyle = NSBorderlessWindowMask;
2061       }
2062     } else if (w->modal()) {
2063       winstyle &= ~NSMiniaturizableWindowMask;
2064       // winstyle &= ~(NSResizableWindowMask | NSMiniaturizableWindowMask);
2065       winlevel = NSModalPanelWindowLevel;
2066     }
2067     else if (w->non_modal()) {
2068       winlevel = NSFloatingWindowLevel;
2069     }
2070     
2071     if (by+bt) {
2072       wp += 2*bx;
2073       hp += 2*by+bt;
2074     }
2075     if (!(w->flags() & Fl_Window::FORCE_POSITION)) {
2076       // use the Carbon functions below for default window positioning
2077       w->x(xyPos+Fl::x());
2078       w->y(xyPos+Fl::y());
2079       xyPos += 25;
2080       if (xyPos>200) xyPos = 100;
2081     } else {
2082       if (!Fl::grab()) {
2083         xp = xwm; yp = ywm;
2084         w->x(xp);w->y(yp);
2085       }
2086       xp -= bx;
2087       yp -= by+bt;
2088     }
2089     
2090     if (w->non_modal() && Fl_X::first /*&& !fl_disable_transient_for*/) {
2091       // find some other window to be "transient for":
2092       Fl_Window* w = Fl_X::first->w;
2093       while (w->parent()) w = w->window(); // todo: this code does not make any sense! (w!=w??)
2094     }
2095         
2096     Fl_X* x = new Fl_X;
2097     x->subwindow = false;
2098     x->other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows
2099     x->region = 0;
2100     x->subRegion = 0;
2101     x->cursor = fl_default_cursor;
2102     x->xidChildren = 0;
2103     x->xidNext = 0;
2104     x->gc = 0;
2105           
2106     NSRect srect = [[NSScreen mainScreen] frame];
2107     NSRect crect;
2108     crect.origin.x = w->x(); 
2109     crect.origin.y = srect.size.height - (w->y() + w->h());
2110     crect.size.width=w->w(); 
2111     crect.size.height=w->h();
2112     FLWindow *cw = [[FLWindow alloc] initWithFl_W:w 
2113                                       contentRect:crect  
2114                                         styleMask:winstyle];
2115     [cw setHasShadow:YES];
2116     [cw setAcceptsMouseMovedEvents:YES];
2117     x->xid = cw;
2118     FLView *myview = [[FLView alloc] init];
2119     [cw setContentView:myview];
2120     [cw setLevel:winlevel];
2121     
2122     q_set_window_title(cw, w->label(), w->iconlabel());
2123     if (!(w->flags() & Fl_Window::FORCE_POSITION)) {
2124       if (w->modal()) {
2125         [cw center];
2126       } else if (w->non_modal()) {
2127         [cw center];
2128       } else {
2129         static NSPoint delta = NSZeroPoint;
2130         delta = [cw cascadeTopLeftFromPoint:delta];
2131       }
2132     }
2133     if(w->menu_window()) { // make menu windows slightly transparent
2134       [cw setAlphaValue:0.97];
2135     }
2136     x->w = w; w->i = x;
2137     x->wait_for_expose = 1;
2138     x->next = Fl_X::first;
2139     Fl_X::first = x;
2140     // Install DnD handlers 
2141     [myview registerForDraggedTypes:[NSArray arrayWithObjects:
2142                                      NSStringPboardType,  NSFilenamesPboardType, nil]];
2143     if ( ! Fl_X::first->next ) {        
2144       // if this is the first window, we need to bring the application to the front
2145       ProcessSerialNumber psn = { 0, kCurrentProcess };
2146       SetFrontProcess( &psn );
2147     }
2148     
2149     if (w->size_range_set) w->size_range_();
2150     
2151     if ( w->border() || (!w->modal() && !w->tooltip_window()) ) {
2152       Fl_Tooltip::enter(0);
2153     }
2154     w->set_visible();
2155     if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle(FL_FOCUS, w);
2156     Fl::first_window(w);
2157     [cw setDelegate:mydelegate];
2158     if (fl_show_iconic) { 
2159       fl_show_iconic = 0;
2160       [cw miniaturize:nil];
2161     } else {
2162       [cw makeKeyAndOrderFront:nil];
2163     }
2164     
2165     crect = [[cw contentView] frame];
2166     w->w(int(crect.size.width));
2167     w->h(int(crect.size.height));
2168     crect = [cw frame];
2169     w->x(int(crect.origin.x));
2170     srect = [[cw screen] frame];
2171     w->y(int(srect.size.height - (crect.origin.y + w->h())));
2172     
2173     int old_event = Fl::e_number;
2174     w->handle(Fl::e_number = FL_SHOW);
2175     Fl::e_number = old_event;
2176     
2177     if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); }
2178   }
2183  * Tell the OS what window sizes we want to allow
2184  */
2185 void Fl_Window::size_range_() {
2186   int bx, by, bt;
2187   get_window_frame_sizes(bx, by, bt);
2188   size_range_set = 1;
2189   NSSize minSize = { minw, minh + bt };
2190   NSSize maxSize = { maxw?maxw:32000, maxh?maxh + bt:32000 };
2191   if (i && i->xid) {
2192     [(NSWindow*)i->xid setMinSize:minSize];
2193     [(NSWindow*)i->xid setMaxSize:maxSize];
2194   }
2199  * returns pointer to the filename, or null if name ends with ':'
2200  */
2201 const char *fl_filename_name( const char *name ) 
2203   const char *p, *q;
2204   if (!name) return (0);
2205   for ( p = q = name ; *p ; ) {
2206     if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) {
2207       q = p+2;
2208       p++;
2209     }
2210     else if (p[0] == '/') {
2211       q = p + 1;
2212     }
2213     p++;
2214   }
2215   return q;
2220  * set the window title bar name
2221  */
2222 void Fl_Window::label(const char *name, const char *mininame) {
2223   Fl_Widget::label(name);
2224   iconlabel_ = mininame;
2225   if (shown() || i) {
2226     NSWindow* nsw = (NSWindow*)i->xid;
2227     q_set_window_title(nsw, name, mininame);
2228   }
2233  * make a window visible
2234  */
2235 void Fl_Window::show() {
2236   image(Fl::scheme_bg_);
2237   if (Fl::scheme_bg_) {
2238     labeltype(FL_NORMAL_LABEL);
2239     align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
2240   } else {
2241     labeltype(FL_NO_LABEL);
2242   }
2243   Fl_Tooltip::exit(this);
2244   if (!shown() || !i) {
2245     Fl_X::make(this);
2246   } else {
2247     if ( !parent() ) {
2248       if ([(NSWindow*)i->xid isMiniaturized]) {
2249         i->w->redraw();
2250         [(NSWindow*)i->xid deminiaturize:nil];
2251       }
2252       if (!fl_capture) {
2253         [(NSWindow*)i->xid makeKeyAndOrderFront:nil];
2254       }
2255     }
2256   }
2261  * resize a window
2262  */
2263 void Fl_Window::resize(int X,int Y,int W,int H) {
2264   if (W<=0) W = 1; // OS X does not like zero width windows
2265   if (H<=0) H = 1;
2266   int is_a_resize = (W != w() || H != h());
2267   //  printf("Fl_Window::resize(X=%d, Y=%d, W=%d, H=%d), is_a_resize=%d, resize_from_system=%p, this=%p\n",
2268   //         X, Y, W, H, is_a_resize, resize_from_system, this);
2269   if (X != x() || Y != y()) set_flag(FORCE_POSITION);
2270   else if (!is_a_resize) return;
2271   if ( (resize_from_system!=this) && (!parent()) && shown()) {
2272     if (is_a_resize) {
2273       if (resizable()) {
2274         if (W<minw) minw = W; // user request for resize takes priority
2275         if (maxw && W>maxw) maxw = W; // over a previously set size_range
2276         if (H<minh) minh = H;
2277         if (maxh && H>maxh) maxh = H;
2278         size_range(minw, minh, maxw, maxh);
2279       } else {
2280         size_range(W, H, W, H);
2281       }
2282       int bx, by, bt;
2283       if ( ! this->border() ) bt = 0;
2284       else get_window_frame_sizes(bx, by, bt);
2285       NSRect dim;
2286       dim.origin.x = X;
2287       dim.origin.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + H);
2288       dim.size.width = W;
2289       dim.size.height = H + bt;
2290       [(NSWindow*)i->xid setFrame:dim display:YES];
2291     } else {
2292       NSPoint pt; 
2293       pt.x = X; 
2294       pt.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + h());
2295       [(NSWindow*)i->xid setFrameOrigin:pt];
2296     }
2297   }
2298   resize_from_system = 0;
2299   if (is_a_resize) {
2300     Fl_Group::resize(X,Y,W,H);
2301     if (shown()) { 
2302       redraw(); 
2303     }
2304   } else {
2305     x(X); y(Y); 
2306   }
2311  * make all drawing go into this window (called by subclass flush() impl.)
2312  */
2313 void Fl_Window::make_current() 
2315   Fl_X::q_release_context();
2316   fl_window = i->xid;
2317   current_ = this;
2318   
2319   int xp = 0, yp = 0;
2320   Fl_Window *win = this;
2321   while ( win ) {
2322     if ( !win->window() )
2323       break;
2324     xp += win->x();
2325     yp += win->y();
2326     win = (Fl_Window*)win->window();
2327   }
2328   
2329   NSView *current_focus = [NSView focusView]; 
2330   // sometimes current_focus is set to a non-FLTK view: don't touch that
2331   if ( [current_focus isKindOfClass:[FLView class]] ) [current_focus unlockFocus];
2332   [[(NSWindow*)i->xid contentView]  lockFocus];
2333   i->gc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2334   fl_gc = i->gc;
2335   Fl_Region fl_window_region = XRectangleRegion(0,0,w(),h());
2336   if ( ! this->window() ) {
2337     for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) {   // clip-out all sub-windows
2338       Fl_Window *cw = cx->w;
2339       Fl_Region from = fl_window_region;
2340       fl_window_region = MacRegionMinusRect(from, cw->x(), cw->y(), cw->w(), cw->h() );
2341       XDestroyRegion(from);
2342     }
2343   }
2344   
2345   // antialiasing must be deactivated because it applies to rectangles too
2346   // and escapes even clipping!!!
2347   // it gets activated when needed (e.g., draw text)
2348   CGContextSetShouldAntialias(fl_gc, false);  
2349   CGFloat hgt = [[(NSWindow*)fl_window contentView] frame].size.height;
2350   CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f);
2351   CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the window
2352   win = this;
2353   while(win && win->window()) { // translate to subwindow origin if this is a subwindow context
2354     CGContextTranslateCTM(fl_gc, win->x(), win->y());
2355     win = win->window();
2356   }
2357   //apply window's clip
2358   CGContextClipToRects(fl_gc, fl_window_region->rects, fl_window_region->count );
2359   XDestroyRegion(fl_window_region);
2360 // this is the context with origin at top left of (sub)window clipped out of its subwindows if any
2361   CGContextSaveGState(fl_gc); 
2362   fl_clip_region( 0 );
2363   
2366 // helper function to manage the current CGContext fl_gc
2367 extern void fl_quartz_restore_line_style_();
2369 // FLTK has only one global graphics state. This function copies the FLTK state into the
2370 // current Quartz context
2371 void Fl_X::q_fill_context() {
2372   if (!fl_gc) return;
2373   if ( ! fl_window) { // a bitmap context
2374     size_t hgt = CGBitmapContextGetHeight(fl_gc);
2375     CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f);
2376     CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the context
2377     }
2378   fl_color(fl_graphics_driver->color());
2379   fl_quartz_restore_line_style_();
2382 // The only way to reset clipping to its original state is to pop the current graphics
2383 // state and restore the global state.
2384 void Fl_X::q_clear_clipping() {
2385   if (!fl_gc) return;
2386   CGContextRestoreGState(fl_gc);
2387   CGContextSaveGState(fl_gc);
2390 // Give the Quartz context back to the system
2391 void Fl_X::q_release_context(Fl_X *x) {
2392   if (x && x->gc!=fl_gc) return;
2393   if (!fl_gc) return;
2394   CGContextRestoreGState(fl_gc); // matches the CGContextSaveGState of make_current
2395   fl_gc = 0;
2398 void Fl_X::q_begin_image(CGRect &rect, int cx, int cy, int w, int h) {
2399   CGContextSaveGState(fl_gc);
2400   CGRect r2 = rect;
2401   r2.origin.x -= 0.5f;
2402   r2.origin.y -= 0.5f;
2403   CGContextClipToRect(fl_gc, r2);
2404   // move graphics context to origin of vertically reversed image 
2405   CGContextTranslateCTM(fl_gc, rect.origin.x - cx - 0.5, rect.origin.y - cy + h - 0.5);
2406   CGContextScaleCTM(fl_gc, 1, -1);
2407   rect.origin.x = rect.origin.y = 0;
2408   rect.size.width = w;
2409   rect.size.height = h;
2412 void Fl_X::q_end_image() {
2413   CGContextRestoreGState(fl_gc);
2417 ////////////////////////////////////////////////////////////////
2418 // Copy & Paste fltk implementation.
2419 ////////////////////////////////////////////////////////////////
2421 static void convert_crlf(char * s, size_t len)
2423   // turn all \r characters into \n:
2424   for (size_t x = 0; x < len; x++) if (s[x] == '\r') s[x] = '\n';
2427 // fltk 1.3 clipboard support constant definitions:
2428 const CFStringRef       flavorNames[] = {
2429   CFSTR("public.utf16-plain-text"), 
2430   CFSTR("public.utf8-plain-text"),
2431   CFSTR("com.apple.traditional-mac-plain-text") };
2432 const CFStringEncoding encodings[] = { 
2433   kCFStringEncodingUnicode, 
2434   kCFStringEncodingUTF8, 
2435   kCFStringEncodingMacRoman};
2436 const size_t handledFlavorsCount = sizeof(encodings)/sizeof(CFStringEncoding);
2438 // clipboard variables definitions :
2439 char *fl_selection_buffer[2];
2440 int fl_selection_length[2];
2441 static int fl_selection_buffer_length[2];
2443 static PasteboardRef myPasteboard = 0;
2444 static void allocatePasteboard() {
2445   if (!myPasteboard)
2446     PasteboardCreate(kPasteboardClipboard, &myPasteboard);
2451  * create a selection
2452  * owner: widget that created the selection
2453  * stuff: pointer to selected data
2454  * size of selected data
2455  */
2456 void Fl::copy(const char *stuff, int len, int clipboard) {
2457   if (!stuff || len<0) return;
2458   if (len+1 > fl_selection_buffer_length[clipboard]) {
2459     delete[] fl_selection_buffer[clipboard];
2460     fl_selection_buffer[clipboard] = new char[len+100];
2461     fl_selection_buffer_length[clipboard] = len+100;
2462   }
2463   memcpy(fl_selection_buffer[clipboard], stuff, len);
2464   fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
2465   fl_selection_length[clipboard] = len;
2466   if (clipboard) {
2467     allocatePasteboard();
2468     OSStatus err = PasteboardClear(myPasteboard);
2469     if (err!=noErr) return; // clear did not work, maybe not owner of clipboard.
2470     PasteboardSynchronize(myPasteboard);
2471     CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len);
2472     if (text==NULL) return; // there was a pb creating the object, abort.
2473     err=PasteboardPutItemFlavor(myPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), text, 0);
2474     CFRelease(text);
2475   }
2478 // Call this when a "paste" operation happens:
2479 void Fl::paste(Fl_Widget &receiver, int clipboard) {
2480   if (clipboard) {
2481     // see if we own the selection, if not go get it:
2482     fl_selection_length[1] = 0;
2483     OSStatus err = noErr;
2484     Boolean found = false;
2485     CFDataRef flavorData = NULL;
2486     CFStringEncoding encoding = 0;
2487     
2488     allocatePasteboard();
2489     PasteboardSynchronize(myPasteboard);
2490     ItemCount nFlavor = 0, i, j;
2491     err = PasteboardGetItemCount(myPasteboard, &nFlavor);
2492     if (err==noErr) {
2493       for (i=1; i<=nFlavor; i++) {
2494         PasteboardItemID itemID = 0;
2495         CFArrayRef flavorTypeArray = NULL;
2496         found = false;
2497         err = PasteboardGetItemIdentifier(myPasteboard, i, &itemID);
2498         if (err!=noErr) continue;
2499         err = PasteboardCopyItemFlavors(myPasteboard, itemID, &flavorTypeArray);
2500         if (err!=noErr) {
2501           if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
2502           continue;
2503         }
2504         CFIndex flavorCount = CFArrayGetCount(flavorTypeArray);
2505         for (j = 0; j < handledFlavorsCount; j++) {
2506           for (CFIndex flavorIndex=0; flavorIndex<flavorCount; flavorIndex++) {
2507             CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
2508             if (UTTypeConformsTo(flavorType, flavorNames[j])) {
2509               err = PasteboardCopyItemFlavorData( myPasteboard, itemID, flavorNames[j], &flavorData );
2510               if (err != noErr) continue;
2511               encoding = encodings[j];
2512               found = true;
2513               break;
2514             }
2515           }
2516           if (found) break;
2517         }
2518         if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
2519         if (found) break;
2520       }
2521       if (found) {
2522         CFIndex len = CFDataGetLength(flavorData);
2523         CFStringRef mycfs = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(flavorData), len, encoding, false);
2524         CFRelease(flavorData);
2525         len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(mycfs), kCFStringEncodingUTF8) + 1;
2526         if ( len >= fl_selection_buffer_length[1] ) {
2527           fl_selection_buffer_length[1] = len;
2528           delete[] fl_selection_buffer[1];
2529           fl_selection_buffer[1] = new char[len];
2530         }
2531         CFStringGetCString(mycfs, fl_selection_buffer[1], len, kCFStringEncodingUTF8);
2532         CFRelease(mycfs);
2533         len = strlen(fl_selection_buffer[1]);
2534         fl_selection_length[1] = len;
2535         convert_crlf(fl_selection_buffer[1],len); // turn all \r characters into \n:
2536       }
2537     }
2538   }
2539   Fl::e_text = fl_selection_buffer[clipboard];
2540   Fl::e_length = fl_selection_length[clipboard];
2541   if (!Fl::e_text) Fl::e_text = (char *)"";
2542   receiver.handle(FL_PASTE);
2545 void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
2547   // check, if this timer slot exists already
2548   for (int i = 0; i < mac_timer_used; ++i) {
2549     MacTimeout& t = mac_timers[i];
2550     // if so, simply change the fire interval
2551     if (t.callback == cb  &&  t.data == data) {
2552       CFRunLoopTimerSetNextFireDate(t.timer, CFAbsoluteTimeGetCurrent() + time );
2553       t.pending = 1;
2554       return;
2555     }
2556   }
2557   // no existing timer to use. Create a new one:
2558   int timer_id = -1;
2559   // find an empty slot in the timer array
2560   for (int i = 0; i < mac_timer_used; ++i) {
2561     if ( !mac_timers[i].timer ) {
2562       timer_id = i;
2563       break;
2564     }
2565   }
2566   // if there was no empty slot, append a new timer
2567   if (timer_id == -1) {
2568     // make space if needed
2569     if (mac_timer_used == mac_timer_alloc) {
2570       realloc_timers();
2571     }
2572     timer_id = mac_timer_used++;
2573   }
2574   // now install a brand new timer
2575   MacTimeout& t = mac_timers[timer_id];
2576   CFRunLoopTimerContext context = {0, data, NULL,NULL,NULL};
2577   CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 
2578                                                     CFAbsoluteTimeGetCurrent() + time,
2579                                                     1E30,  
2580                                                     0,
2581                                                     0,
2582                                                     do_timer,
2583                                                     &context
2584                                                     );
2585   if (timerRef) {
2586     CFRunLoopAddTimer(CFRunLoopGetCurrent(),
2587                       timerRef,
2588                       kCFRunLoopDefaultMode);
2589     t.callback = cb;
2590     t.data     = data;
2591     t.timer    = timerRef;
2592     t.pending  = 1;
2593   }
2596 void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
2598   // currently, repeat_timeout does not subtract the trigger time of the previous timer event as it should.
2599   add_timeout(time, cb, data);
2602 int Fl::has_timeout(Fl_Timeout_Handler cb, void* data)
2604   for (int i = 0; i < mac_timer_used; ++i) {
2605     MacTimeout& t = mac_timers[i];
2606     if (t.callback == cb  &&  t.data == data && t.pending) {
2607       return 1;
2608     }
2609   }
2610   return 0;
2613 void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
2615   for (int i = 0; i < mac_timer_used; ++i) {
2616     MacTimeout& t = mac_timers[i];
2617     if (t.callback == cb  && ( t.data == data || data == NULL)) {
2618       delete_timer(t);
2619     }
2620   }
2623 int Fl_X::unlink(Fl_X *start) {
2624   if (start) {
2625     Fl_X *pc = start;
2626     while (pc) {
2627       if (pc->xidNext == this) {
2628         pc->xidNext = xidNext;
2629         return 1;
2630       }
2631       if (pc->xidChildren) {
2632         if (pc->xidChildren == this) {
2633           pc->xidChildren = xidNext;
2634           return 1;
2635         }
2636         if (unlink(pc->xidChildren))
2637           return 1;
2638       }
2639       pc = pc->xidNext;
2640     }
2641   } else {
2642     for ( Fl_X *pc = Fl_X::first; pc; pc = pc->next ) {
2643       if (unlink(pc))
2644         return 1;
2645     }
2646   }  
2647   return 0;
2650 void Fl_X::relink(Fl_Window *w, Fl_Window *wp) {
2651   Fl_X *x = Fl_X::i(w);
2652   Fl_X *p = Fl_X::i(wp);
2653   if (!x || !p) return;
2654   // first, check if 'x' is already registered as a child of 'p'
2655   for (Fl_X *i = p->xidChildren; i; i=i->xidNext) {
2656     if (i == x) return;
2657   }
2658   // now add 'x' as the first child of 'p'
2659   x->xidNext = p->xidChildren;
2660   p->xidChildren = x;
2663 void Fl_X::destroy() {
2664   // subwindows share their xid with their parent window, so should not close it
2665   if (!subwindow && w && !w->parent() && xid) {
2666     [[(NSWindow *)xid contentView] release];
2667     [(NSWindow *)xid close];
2668   }
2671 void Fl_X::map() {
2672   if (w && xid) {
2673     [(NSWindow *)xid orderFront:nil];
2674   }
2675   //+ link to window list
2676   if (w && w->parent()) {
2677     Fl_X::relink(w, w->window() );
2678     w->redraw();
2679   }
2682 void Fl_X::unmap() {
2683   if (w && !w->parent() && xid) {
2684     [(NSWindow *)xid orderOut:nil];
2685   }
2686   if (w && Fl_X::i(w)) 
2687     Fl_X::i(w)->unlink();
2691 // removes x,y,w,h rectangle from region r and returns result as a new Fl_Region
2692 static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h)
2694   Fl_Region outr = (Fl_Region)malloc(sizeof(*outr));
2695   outr->rects = (CGRect*)malloc(4 * r->count * sizeof(CGRect));
2696   outr->count = 0;
2697   CGRect rect = fl_cgrectmake_cocoa(x, y, w, h);
2698   for( int i = 0; i < r->count; i++) {
2699     CGRect A = r->rects[i];
2700     CGRect test = CGRectIntersection(A, rect);
2701     if (CGRectIsEmpty(test)) {
2702       outr->rects[(outr->count)++] = A;
2703     }
2704     else {
2705       const CGFloat verylarge = 100000.;
2706       CGRect side = CGRectMake(0,0,rect.origin.x,verylarge);// W side
2707       test = CGRectIntersection(A, side);
2708       if ( ! CGRectIsEmpty(test)) {
2709         outr->rects[(outr->count)++] = test;
2710       }
2711       side = CGRectMake(0,rect.origin.y + rect.size.height,verylarge,verylarge);// N side
2712       test = CGRectIntersection(A, side);
2713       if ( ! CGRectIsEmpty(test)) {
2714         outr->rects[(outr->count)++] = test;
2715       }
2716       side = CGRectMake(rect.origin.x + rect.size.width, 0, verylarge, verylarge);// E side
2717       test = CGRectIntersection(A, side);
2718       if ( ! CGRectIsEmpty(test)) {
2719         outr->rects[(outr->count)++] = test;
2720       }
2721       side = CGRectMake(0, 0, verylarge, rect.origin.y);// S side
2722       test = CGRectIntersection(A, side);
2723       if ( ! CGRectIsEmpty(test)) {
2724         outr->rects[(outr->count)++] = test;
2725       }
2726     }
2727   }
2728   if (outr->count == 0) {
2729     free(outr->rects);
2730     free(outr);
2731     outr = XRectangleRegion(0,0,0,0);
2732   }
2733   else outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect));
2734   return outr;
2737 // intersects current and x,y,w,h rectangle and returns result as a new Fl_Region
2738 Fl_Region Fl_X::intersect_region_and_rect(Fl_Region current, int x,int y,int w, int h)
2740   if (current == NULL) return XRectangleRegion(x,y,w,h);
2741   CGRect r = fl_cgrectmake_cocoa(x, y, w, h);
2742   Fl_Region outr = (Fl_Region)malloc(sizeof(*outr));
2743   outr->count = current->count;
2744   outr->rects =(CGRect*)malloc(outr->count * sizeof(CGRect));
2745   int j = 0;
2746   for(int i = 0; i < current->count; i++) {
2747     CGRect test = CGRectIntersection(current->rects[i], r);
2748     if (!CGRectIsEmpty(test)) outr->rects[j++] = test;
2749   }
2750   if (j) {
2751     outr->count = j;
2752     outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect));
2753   }
2754   else {
2755     XDestroyRegion(outr);
2756     outr = XRectangleRegion(0,0,0,0);
2757   }
2758   return outr;
2761 void Fl_X::collapse() {
2762   [(NSWindow *)xid miniaturize:nil];
2765 static NSImage *CGBitmapContextToNSImage(CGContextRef c)
2766 // the returned NSImage is autoreleased
2768   unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c);
2769   NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata
2770                                                                        pixelsWide:CGBitmapContextGetWidth(c)
2771                                                                        pixelsHigh:CGBitmapContextGetHeight(c)
2772                                                                     bitsPerSample:8
2773                                                                   samplesPerPixel:4
2774                                                                          hasAlpha:YES
2775                                                                          isPlanar:NO
2776                                                                    colorSpaceName:NSDeviceRGBColorSpace
2777                                                                       bytesPerRow:CGBitmapContextGetBytesPerRow(c)
2778                                                                      bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)];
2779   NSImage* image = [[NSImage alloc] initWithData: [imagerep TIFFRepresentation]];
2780   [imagerep release];
2781   return [image autorelease];
2784 static NSCursor *PrepareCursor(NSCursor *cursor, CGContextRef (*f)() )
2786   if (cursor == nil) {
2787     CGContextRef c = f();
2788     NSImage *image = CGBitmapContextToNSImage(c);
2789     fl_delete_offscreen( (Fl_Offscreen)c ); 
2790     NSPoint pt = {[image size].width/2, [image size].height/2};
2791     cursor = [[NSCursor alloc] initWithImage:image hotSpot:pt];
2792   }
2793   return cursor;
2796 void Fl_X::set_cursor(Fl_Cursor c)
2798   NSCursor *icrsr;
2799   switch (c) {
2800     case FL_CURSOR_CROSS:  icrsr = [NSCursor crosshairCursor]; break;
2801     case FL_CURSOR_WAIT:
2802       static NSCursor *watch = nil;
2803       watch = PrepareCursor(watch,  &Fl_X::watch_cursor_image);
2804       icrsr = watch;
2805       break;
2806     case FL_CURSOR_INSERT: icrsr = [NSCursor IBeamCursor]; break;
2807     case FL_CURSOR_N:      icrsr = [NSCursor resizeUpCursor]; break;
2808     case FL_CURSOR_S:      icrsr = [NSCursor resizeDownCursor]; break;
2809     case FL_CURSOR_NS:     icrsr = [NSCursor resizeUpDownCursor]; break;
2810     case FL_CURSOR_HELP:   
2811       static NSCursor *help = nil;
2812       help = PrepareCursor(help,  &Fl_X::help_cursor_image);
2813       icrsr = help;
2814       break;
2815     case FL_CURSOR_HAND:   icrsr = [NSCursor pointingHandCursor]; break;
2816     case FL_CURSOR_MOVE:   icrsr = [NSCursor openHandCursor]; break;
2817     case FL_CURSOR_NE:
2818     case FL_CURSOR_SW:
2819     case FL_CURSOR_NESW:   
2820       static NSCursor *nesw = nil;
2821       nesw = PrepareCursor(nesw,  &Fl_X::nesw_cursor_image);
2822       icrsr = nesw;
2823       break;
2824     case FL_CURSOR_E:      icrsr = [NSCursor resizeRightCursor]; break;
2825     case FL_CURSOR_W:      icrsr = [NSCursor resizeLeftCursor]; break;
2826     case FL_CURSOR_WE:     icrsr = [NSCursor resizeLeftRightCursor]; break;
2827     case FL_CURSOR_SE:
2828     case FL_CURSOR_NW:
2829     case FL_CURSOR_NWSE:   
2830       static NSCursor *nwse = nil;
2831       nwse = PrepareCursor(nwse,  &Fl_X::nwse_cursor_image);
2832       icrsr = nwse;
2833       break;
2834     case FL_CURSOR_NONE:   
2835       static NSCursor *none = nil;
2836       none = PrepareCursor(none,  &Fl_X::none_cursor_image);
2837       icrsr = none; 
2838       break;
2839     case FL_CURSOR_ARROW:
2840     case FL_CURSOR_DEFAULT:
2841     default:                       icrsr = [NSCursor arrowCursor];
2842       break;
2843   }
2844   [icrsr set];
2845   cursor = icrsr;
2848 @interface FLaboutItemTarget : NSObject 
2851 - (void)showPanel;
2852 - (void)printPanel;
2853 @end
2854 @implementation FLaboutItemTarget
2855 - (void)showPanel
2857     NSDictionary *options;
2858     options = [NSDictionary dictionaryWithObjectsAndKeys:
2859                              [NSString stringWithFormat:@" GUI with FLTK %d.%d", FL_MAJOR_VERSION,
2860                               FL_MINOR_VERSION ], @"Copyright",
2861                              nil];
2862     [NSApp  orderFrontStandardAboutPanelWithOptions:options];
2863   }
2864 //#include <FL/Fl_PostScript.H>
2865 - (void)printPanel
2867   Fl_Printer printer;
2868   //Fl_PostScript_File_Device printer;
2869   int w, h, ww, wh;
2870   Fl_Window *win = Fl::first_window();
2871   if(!win) return;
2872   if( printer.start_job(1) ) return;
2873   if( printer.start_page() ) return;
2874   // scale the printer device so that the window fits on the page
2875   float scale = 1;
2876   printer.printable_rect(&w, &h);
2877   ww = win->decorated_w();
2878   wh = win->decorated_h();
2879   if (ww>w || wh>h) {
2880     scale = (float)w/win->w();
2881     if ((float)h/wh < scale) scale = (float)h/wh;
2882     printer.scale(scale);
2883   }
2884 //#define ROTATE 1
2885 #ifdef ROTATE
2886   printer.scale(scale * 0.8, scale * 0.8);
2887   printer.printable_rect(&w, &h);
2888   printer.origin(w/2, h/2 );
2889   printer.rotate(20.);
2890   printer.print_widget( win, - win->w()/2, - win->h()/2 );
2891 #else
2892   printer.print_window(win);
2893 #endif
2894   printer.end_page();
2895   printer.end_job();
2897 @end
2899 static void createAppleMenu(void)
2901   static BOOL donethat = NO;
2902   if (donethat) return;
2903   donethat = YES;
2904   NSMenu *mainmenu, *services, *appleMenu;
2905   NSMenuItem *menuItem;
2906   NSString *title;
2908   NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
2909   if (nsappname == nil)
2910     nsappname = [[NSProcessInfo processInfo] processName];
2911   appleMenu = [[NSMenu alloc] initWithTitle:@""];
2912   /* Add menu items */
2913   title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::about] stringByAppendingString:nsappname];
2914   menuItem = [appleMenu addItemWithTitle:title action:@selector(showPanel) keyEquivalent:@""];
2915   FLaboutItemTarget *about = [[FLaboutItemTarget alloc] init];
2916   [menuItem setTarget:about];
2917   [appleMenu addItem:[NSMenuItem separatorItem]];
2918   // Print front window
2919   if (strlen(Fl_Mac_App_Menu::print) > 0) {
2920     menuItem = [appleMenu 
2921                 addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::print] 
2922                 action:@selector(printPanel) 
2923                 keyEquivalent:@""];
2924     [menuItem setTarget:about];
2925     [appleMenu setAutoenablesItems:NO];
2926     [menuItem setEnabled:YES];
2927     [appleMenu addItem:[NSMenuItem separatorItem]];
2928     }
2929   // Services Menu
2930   services = [[NSMenu alloc] init];
2931   menuItem = [appleMenu 
2932               addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::services] 
2933               action:nil 
2934               keyEquivalent:@""];
2935   [appleMenu setSubmenu:services forItem:menuItem];
2936   [appleMenu addItem:[NSMenuItem separatorItem]];
2937   // Hide AppName
2938   title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::hide] stringByAppendingString:nsappname];
2939   [appleMenu addItemWithTitle:title 
2940                        action:@selector(hide:) 
2941                 keyEquivalent:@"h"];
2942   // Hide Others
2943   menuItem = [appleMenu 
2944               addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::hide_others] 
2945               action:@selector(hideOtherApplications:) 
2946               keyEquivalent:@"h"];
2947   [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
2948   // Show All
2949   [appleMenu addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::show] 
2950                        action:@selector(unhideAllApplications:) keyEquivalent:@""];
2951   [appleMenu addItem:[NSMenuItem separatorItem]];
2952   // Quit AppName
2953   title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::quit] 
2954            stringByAppendingString:nsappname];
2955   [appleMenu addItemWithTitle:title 
2956                        action:@selector(terminate:) 
2957                 keyEquivalent:@"q"];
2958   /* Put menu into the menubar */
2959   menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
2960   [menuItem setSubmenu:appleMenu];
2961   mainmenu = [[NSMenu alloc] initWithTitle:@""];
2962   [mainmenu addItem:menuItem];
2963   if (fl_mac_os_version < 100600) {
2964     //  [NSApp setAppleMenu:appleMenu];
2965     //  to avoid compiler warning raised by use of undocumented setAppleMenu    :
2966     [NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu];
2967   }
2968   [NSApp setServicesMenu:services];
2969   [NSApp setMainMenu:mainmenu];
2970   [services release];
2971   [mainmenu release];
2972   [appleMenu release];
2973   [menuItem release];
2976 @interface FLMenuItem : NSMenuItem {
2978 - (void) doCallback:(id)unused;
2979 - (void) directCallback:(id)unused;
2980 @end
2981 @implementation FLMenuItem
2982 - (void) doCallback:(id)unused
2984   int flRank = [self tag];
2985   const Fl_Menu_Item *items = fl_sys_menu_bar->Fl_Menu_::menu();
2986   const Fl_Menu_Item *item = items + flRank;
2987   if (item) {
2988     fl_sys_menu_bar->picked(item);
2989     if ( item->flags & FL_MENU_TOGGLE ) {       // update the menu toggle symbol
2990       [self setState:(item->value() ? NSOnState : NSOffState)];
2991     }
2992     else if ( item->flags & FL_MENU_RADIO ) {   // update the menu radio symbols
2993       int from = flRank;
2994       while( from > 0 && items[from - 1].label() && (items[from - 1].flags & FL_MENU_RADIO) &&
2995             !(items[from - 1].flags & FL_MENU_DIVIDER) ) {
2996         from--;
2997       }
2998       int to = flRank;
2999       while( !(items[to].flags & FL_MENU_DIVIDER) && items[to + 1].label() && 
3000             (items[to + 1].flags & FL_MENU_RADIO) ) {
3001         to++;
3002       }
3003       NSMenu *nsmenu = [self menu];
3004       int nsrank = (int)[nsmenu indexOfItem:self];
3005       for(int i =  from - flRank + nsrank ; i <= to - flRank + nsrank; i++) {
3006         NSMenuItem *nsitem = [nsmenu itemAtIndex:i];
3007         if (nsitem != self) [nsitem setState:NSOffState];
3008         else [nsitem setState:(item->value() ? NSOnState : NSOffState) ];
3009       }
3010     }
3011   }
3013 - (void) directCallback:(id)unused
3015   Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes];
3016   if ( item && item->callback() ) item->do_callback(NULL);
3018 @end
3020 void fl_mac_set_about( Fl_Callback *cb, void *user_data, int shortcut) 
3022   fl_open_display();
3023   Fl_Menu_Item aboutItem;
3024   memset(&aboutItem, 0, sizeof(Fl_Menu_Item));
3025   aboutItem.callback(cb);
3026   aboutItem.user_data(user_data);
3027   aboutItem.shortcut(shortcut);
3028   NSMenu *appleMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu];
3029   CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]);
3030   [appleMenu removeItemAtIndex:0];
3031   FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname 
3032                                                  action:@selector(directCallback:) 
3033                                           keyEquivalent:@""] autorelease];
3034   if (aboutItem.shortcut()) {
3035     Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalent, item, aboutItem.shortcut() & 0xff);
3036     Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask, item, aboutItem.shortcut() );
3037   }
3038   NSData *pointer = [NSData dataWithBytes:&aboutItem length:sizeof(Fl_Menu_Item)];
3039   [item setRepresentedObject:pointer];
3040   [appleMenu insertItem:item atIndex:0];
3041   CFRelease(cfname);
3042   [item setTarget:item];
3045 static char *remove_ampersand(const char *s)
3047   char *ret = strdup(s);
3048   const char *p = s;
3049   char *q = ret;
3050   while(*p != 0) {
3051     if (p[0]=='&') {
3052       if (p[1]=='&') {
3053         *q++ = '&'; p+=2;
3054       } else {
3055         p++;
3056       }
3057     } else {
3058       *q++ = *p++;
3059     }
3060   }
3061   *q = 0;
3062   return ret;
3065 void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperation operation, ...)
3066 /* these operations apply to menus, submenus, or menu items
3067  */
3069   NSAutoreleasePool *localPool;
3070   localPool = [[NSAutoreleasePool alloc] init]; 
3071   NSMenu *menu;
3072   NSMenuItem *item;
3073   int value;
3074   void *pter;
3075   void *retval = NULL;
3076   va_list ap;
3077   va_start(ap, operation);
3078   
3079   if (operation == Fl_Sys_Menu_Bar::itemAtIndex) {      // arguments: NSMenu*, int. Returns the item
3080     menu = va_arg(ap, NSMenu*);
3081     value = va_arg(ap, int);
3082     retval = (void *)[menu itemAtIndex:value];
3083   }
3084   else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalent) {    // arguments: NSMenuItem*, int
3085     item = va_arg(ap, NSMenuItem*);
3086     value = va_arg(ap, int);
3087     char key = value;
3088     NSString *equiv = [[NSString alloc] initWithBytes:&key length:1 encoding:NSASCIIStringEncoding];
3089     [item setKeyEquivalent:equiv];
3090     [equiv release];
3091   }
3092   else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask) {                // arguments: NSMenuItem*, int
3093     item = va_arg(ap, NSMenuItem*);
3094     value = va_arg(ap, int);
3095     NSUInteger macMod = 0;
3096     if ( value & FL_META ) macMod = NSCommandKeyMask;
3097     if ( value & FL_SHIFT || isupper(value) ) macMod |= NSShiftKeyMask;
3098     if ( value & FL_ALT ) macMod |= NSAlternateKeyMask;
3099     if ( value & FL_CTRL ) macMod |= NSControlKeyMask;
3100     [item setKeyEquivalentModifierMask:macMod];
3101   }
3102   else if (operation == Fl_Sys_Menu_Bar::setState) {    // arguments: NSMenuItem*, int
3103     item = va_arg(ap, NSMenuItem*);
3104     value = va_arg(ap, int);
3105     [item setState:(value ? NSOnState : NSOffState)];
3106   }
3107   else if (operation == Fl_Sys_Menu_Bar::initWithTitle) {       // arguments: const char*title. Returns the newly created menu
3108                                                                 // creates a new (sub)menu
3109     char *ts = remove_ampersand(va_arg(ap, char *));
3110     CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8);
3111     free(ts);
3112     NSMenu *menu = [[NSMenu alloc] initWithTitle:(NSString*)title];
3113     CFRelease(title);
3114     [menu setAutoenablesItems:NO];
3115     retval = (void *)menu;
3116   }
3117   else if (operation == Fl_Sys_Menu_Bar::numberOfItems) {       // arguments: NSMenu *menu, int *pcount
3118                                                                 // upon return, *pcount is set to menu's item count
3119     menu = va_arg(ap, NSMenu*);
3120     pter = va_arg(ap, void *);
3121     *(int*)pter = [menu numberOfItems];
3122   }
3123   else if (operation == Fl_Sys_Menu_Bar::setSubmenu) {          // arguments: NSMenuItem *item, NSMenu *menu
3124                                                                 // sets 'menu' as submenu attached to 'item'
3125     item = va_arg(ap, NSMenuItem*);
3126     menu = va_arg(ap, NSMenu*);
3127     [item setSubmenu:menu];
3128     [menu release];
3129   }
3130   else if (operation == Fl_Sys_Menu_Bar::setEnabled) {          // arguments: NSMenuItem*, int
3131     item = va_arg(ap, NSMenuItem*);
3132     value = va_arg(ap, int);
3133     [item setEnabled:(value ? YES : NO)];
3134   }
3135   else if (operation == Fl_Sys_Menu_Bar::addSeparatorItem) {    // arguments: NSMenu*
3136     menu = va_arg(ap, NSMenu*);
3137     [menu addItem:[NSMenuItem separatorItem]];
3138   }
3139   else if (operation == Fl_Sys_Menu_Bar::setTitle) {            // arguments: NSMenuItem*, const char *
3140     item = va_arg(ap, NSMenuItem*);
3141     char *ts = remove_ampersand(va_arg(ap, char *));
3142     CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8);
3143     free(ts);
3144     [item setTitle:(NSString*)title];
3145     CFRelease(title);
3146   }
3147   else if (operation == Fl_Sys_Menu_Bar::removeItem) {          // arguments: NSMenu*, int
3148     menu = va_arg(ap, NSMenu*);
3149     value = va_arg(ap, int);
3150     [menu removeItem:[menu itemAtIndex:value]];
3151   }
3152   else if (operation == Fl_Sys_Menu_Bar::addNewItem) {          // arguments: NSMenu *menu, int flrank, int *prank
3153     // creates a new menu item at the end of 'menu'
3154     // attaches the item of rank flrank (counted in Fl_Menu_) of fl_sys_menu_bar to it
3155     // upon return, puts the rank (counted in NSMenu) of the new item in *prank unless prank is NULL
3156     menu = va_arg(ap, NSMenu*);
3157     int flRank = va_arg(ap, int);
3158     char *name = remove_ampersand( (fl_sys_menu_bar->Fl_Menu_::menu() + flRank)->label());
3159     int *prank = va_arg(ap, int*);
3160     CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
3161     free(name);
3162     FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:(NSString*)cfname 
3163                                                   action:@selector(doCallback:) 
3164                                            keyEquivalent:@""];
3165     [item setTag:flRank];
3166     [menu addItem:item];
3167     CFRelease(cfname);
3168     [item setTarget:item];
3169     if (prank != NULL) *prank = [menu indexOfItem:item];
3170     [item release];
3171   }
3172   else if (operation == Fl_Sys_Menu_Bar::renameItem) {          // arguments: int rank, const char *newname
3173     // renames the system menu item numbered rank in fl_sys_menu_bar->menu()
3174     int rank = va_arg(ap, int);
3175     char *newname = remove_ampersand( va_arg(ap, const char *) );
3176     int countmenus = [[NSApp mainMenu] numberOfItems];
3177     bool found = NO;
3178     NSMenuItem *macitem = 0;
3179     for(int i = 1; (!found) && i < countmenus; i++) {
3180       NSMenuItem *item = [[NSApp mainMenu] itemAtIndex:i];
3181       NSMenu *submenu = [item submenu];
3182       if (submenu == nil) continue;
3183       int countitems = [submenu numberOfItems];
3184       for(int j = 0; j < countitems; j++) {
3185         macitem = [submenu itemAtIndex:j];
3186         if ([macitem tag] == rank) { found = YES; break; }
3187       }
3188     }
3189     if (found) {
3190       [macitem setTitle:[[[NSString alloc] initWithUTF8String:newname] autorelease]];
3191     }
3192     free(newname);
3193   }
3194   va_end(ap);
3195   [localPool release];
3196   return retval;
3199 void Fl_X::set_key_window()
3201   [(NSWindow*)xid makeKeyWindow];
3204 static NSImage *imageFromText(const char *text, int *pwidth, int *pheight)
3206   const char *p, *q;
3207   int width = 0, height, w2, ltext = strlen(text);
3208   fl_font(FL_HELVETICA, 10);
3209   p = text;
3210   int nl = 0;
3211   while((q=strchr(p, '\n')) != NULL) { 
3212     nl++; 
3213     w2 = int(fl_width(p, q - p));
3214     if (w2 > width) width = w2;
3215     p = q + 1; 
3216   }
3217   if (text[ ltext - 1] != '\n') {
3218     nl++;
3219     w2 = int(fl_width(p));
3220     if (w2 > width) width = w2;
3221   }
3222   height = nl * fl_height() + 3;
3223   width += 6;
3224   Fl_Offscreen off = fl_create_offscreen_with_alpha(width, height);
3225   fl_begin_offscreen(off);
3226   CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0);
3227   fl_rectf(0,0,width,height);
3228   fl_color(FL_BLACK);
3229   p = text;
3230   int y = fl_height();
3231   while(TRUE) {
3232     q = strchr(p, '\n');
3233     if (q) {
3234       fl_draw(p, q - p, 3, y);
3235     } else {
3236       fl_draw(p, 3, y);
3237       break;
3238     }
3239     y += fl_height();
3240     p = q + 1;
3241   }
3242   fl_end_offscreen();
3243   NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off );
3244   fl_delete_offscreen( off );
3245   *pwidth = width;
3246   *pheight = height;
3247   return image;
3250 static NSImage *defaultDragImage(int *pwidth, int *pheight)
3252   const int width = 16, height = 16;
3253   Fl_Offscreen off = fl_create_offscreen_with_alpha(width, height);
3254   fl_begin_offscreen(off);
3255   CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0);
3256   fl_rectf(0,0,width,height);
3257   CGContextSetRGBStrokeColor( (CGContextRef)off, 0,0,0,0.6);
3258   fl_rect(0,0,width,height);
3259   fl_rect(2,2,width-4,height-4);
3260   fl_end_offscreen();
3261   NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off );
3262   fl_delete_offscreen( off );
3263   *pwidth = width;
3264   *pheight = height;
3265   return image;
3268 int Fl::dnd(void)
3270   CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[0], fl_selection_length[0]);
3271   if (text==NULL) return false;
3272   NSAutoreleasePool *localPool;
3273   localPool = [[NSAutoreleasePool alloc] init]; 
3274   NSPasteboard *mypasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
3275   [mypasteboard declareTypes:[NSArray arrayWithObjects:@"public.utf8-plain-text", nil] owner:nil];
3276   [mypasteboard setData:(NSData*)text forType:@"public.utf8-plain-text"];
3277   CFRelease(text);
3278   Fl_Widget *w = Fl::pushed();
3279   Fl_Window *win = w->window();
3280   if (win == NULL) {
3281     win = (Fl_Window*)w;
3282   } else { 
3283     while(win->window()) win = win->window();
3284   }
3285   NSView *myview = [(NSWindow*)Fl_X::i(win)->xid contentView];
3286   NSEvent *theEvent = [NSApp currentEvent];
3287   
3288   int width, height;
3289   NSImage *image;
3290   if ( dynamic_cast<Fl_Input_*>(w) != NULL ||  dynamic_cast<Fl_Text_Display*>(w) != NULL) {
3291     fl_selection_buffer[0][ fl_selection_length[0] ] = 0;
3292     image = imageFromText(fl_selection_buffer[0], &width, &height);
3293   } else {
3294     image = defaultDragImage(&width, &height);
3295   }
3296   
3297   static NSSize offset={0,0};
3298   NSPoint pt = [theEvent locationInWindow];
3299   pt.x -= width/2;
3300   pt.y -= height/2;
3301   [myview dragImage:image  at:pt  offset:offset 
3302               event:theEvent  pasteboard:mypasteboard  
3303              source:myview  slideBack:YES];
3304   if ( w ) {
3305     int old_event = Fl::e_number;
3306     w->handle(Fl::e_number = FL_RELEASE);
3307     Fl::e_number = old_event;
3308     Fl::pushed( 0 );
3309   }
3310   [localPool release];
3311   return true;
3314 unsigned char *Fl_X::bitmap_from_window_rect(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel)
3315 // delete the returned pointer after use
3317   while(win->window()) {
3318     x += win->x();
3319     y += win->y();
3320     win = win->window();
3321   }
3322   CGFloat epsilon = 0;
3323   if (fl_mac_os_version >= 100600) epsilon = 0.001;
3324   // The epsilon offset is absolutely necessary under 10.6. Without it, the top pixel row and
3325   // left pixel column are not read, and bitmap is read shifted by one pixel in both directions. 
3326   // Under 10.5, we want no offset.
3327   NSRect rect = NSMakeRect(x - epsilon, y - epsilon, w, h);
3328   NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect];
3329   *bytesPerPixel = [bitmap bitsPerPixel]/8;
3330   int bpp = (int)[bitmap bytesPerPlane];
3331   int bpr = (int)[bitmap bytesPerRow];
3332   int hh = bpp/bpr; // sometimes hh = h-1 for unclear reason
3333   int ww = bpr/(*bytesPerPixel); // sometimes ww = w-1
3334   unsigned char *data = new unsigned char[w * h *  *bytesPerPixel];
3335   if (w == ww) {
3336     memcpy(data, [bitmap bitmapData], w * hh *  *bytesPerPixel);
3337   } else {
3338     unsigned char *p = [bitmap bitmapData];
3339     unsigned char *q = data;
3340     for(int i = 0;i < hh; i++) {
3341       memcpy(q, p, *bytesPerPixel * ww);
3342       p += bpr;
3343       q += w * *bytesPerPixel;
3344       }
3345   }
3346   [bitmap release];
3347   return data;
3350 static void imgProviderReleaseData (void *info, const void *data, size_t size)
3352   delete[] (unsigned char *)data;
3355 CGImageRef Fl_X::CGImage_from_window_rect(Fl_Window *win, int x, int y, int w, int h)
3356 // CFRelease the returned CGImageRef after use
3358   int bpp;
3359   unsigned char *bitmap = bitmap_from_window_rect(win, x, y, w, h, &bpp);
3360   CGImageRef img;
3361   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
3362   CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmap, w*h*bpp, imgProviderReleaseData);
3363   img = CGImageCreate(w, h, 8, 8*bpp, w*bpp, lut,
3364                       bpp == 3 ? kCGImageAlphaNone : kCGImageAlphaLast,
3365                       provider, NULL, false, kCGRenderingIntentDefault);
3366   CGColorSpaceRelease(lut);
3367   CGDataProviderRelease(provider);
3368   return img;
3371 WindowRef Fl_X::window_ref()
3373   return (WindowRef)[(FLWindow*)xid windowRef];
3376 // so a CGRect matches exactly what is denoted x,y,w,h for clipping purposes
3377 CGRect fl_cgrectmake_cocoa(int x, int y, int w, int h) {
3378   if (Fl_Surface_Device::surface()->class_name() == Fl_Printer::class_id) return CGRectMake(x-0.5, y-0.5, w, h); 
3379   return CGRectMake(x, y, w > 0 ? w - 0.9 : 0, h > 0 ? h - 0.9 : 0);
3382 Window fl_xid(const Fl_Window* w)
3384   Fl_X *temp = Fl_X::i(w);
3385   return temp ? temp->xid : 0;
3388 int Fl_Window::decorated_w()
3390   if (!shown() || parent() || !border() || !visible()) return w();
3391   int bx, by, bt;
3392   get_window_frame_sizes(bx, by, bt);
3393   return w() + 2 * bx;
3396 int Fl_Window::decorated_h()
3398   if (!shown() || parent() || !border() || !visible()) return h();
3399   int bx, by, bt;
3400   get_window_frame_sizes(bx, by, bt);
3401   return h() + bt + by;
3404 void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
3406   if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
3407     this->print_widget(win, x_offset, y_offset);
3408     return;
3409   }
3410   int bx, by, bt;
3411   get_window_frame_sizes(bx, by, bt);
3412   Fl_Display_Device::display_device()->set_current(); // send win to front and make it current
3413   win->show();
3414   fl_gc = NULL;
3415   Fl::check();
3416   win->make_current();
3417   // capture the window title bar from screen
3418   CGImageRef img = Fl_X::CGImage_from_window_rect(win, 0, -bt, win->w(), bt);
3419   this->set_current(); // back to the Fl_Paged_Device
3420   CGRect rect = { { 0, 0 }, { win->w(), bt } }; // print the title bar
3421   Fl_X::q_begin_image(rect, 0, 0, win->w(), bt);
3422   CGContextDrawImage(fl_gc, rect, img);
3423   Fl_X::q_end_image();
3424   CGImageRelease(img);
3425   this->print_widget(win, x_offset, y_offset + bt); // print the window inner part
3428 #include <dlfcn.h>
3430 /* Returns the address of a Carbon function after dynamically loading the Carbon library if needed.
3431  Supports old Mac OS X versions that may use a couple of Carbon calls:
3432  GetKeys used by OS X 10.3 or before (in Fl::get_key())
3433  PMSessionPageSetupDialog and PMSessionPrintDialog used by 10.4 or before (in Fl_Printer::start_job())
3434  GetWindowPort used by 10.4 or before (in Fl_Gl_Choice.cxx)
3435  */
3436 void *Fl_X::get_carbon_function(const char *function_name) {
3437   static void *carbon = NULL;
3438   void *f = NULL;
3439   if (!carbon) {
3440     carbon = dlopen("/System/Library/Frameworks/Carbon.framework/Carbon", RTLD_LAZY);
3441   }
3442   if (carbon) {
3443     f = dlsym(carbon, function_name);
3444   }
3445   return f;
3447   
3448 #endif // __APPLE__
3451 // End of "$Id: Fl_cocoa.mm 8807 2011-06-16 12:35:32Z manolo $".