fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_mac.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_mac.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $"
00003 //
00004 // MacOS specific code for the Fast Light Tool Kit (FLTK).
00005 //
00006 // Copyright 1998-2010 by Bill Spitzak and others.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00029 // (without permission)
00030 //
00031 // "Three Compiles for 68Ks under the sky,
00032 // Seven Compiles for PPCs in their fragments of code,
00033 // Nine Compiles for Mortal Carbon doomed to die,
00034 // One Compile for Mach-O Cocoa on its Mach-O throne,
00035 // in the Land of MacOS X where the Drop-Shadows lie.
00036 // 
00037 // One Compile to link them all, One Compile to merge them,
00038 // One Compile to copy them all and in the bundle bind them,
00039 // in the Land of MacOS X where the Drop-Shadows lie."
00040 
00041 // warning: the Apple Quartz version still uses some Quickdraw calls,
00042 //          mostly to get around the single active context in QD and 
00043 //          to implement clipping. This should be changed into pure
00044 //          Quartz calls in the near future.
00045 
00046 // FIXME moving away from Carbon, I am replacing the Scrap manager calls with Pasteboard
00047 //       calls that support utf8 encoding. As soon as these function haven proven working
00048 //       the Scrap manager calls should be removed
00049 #define USE_PASTEBOARD 1
00050 
00051 // we don't need the following definition because we deliver only
00052 // true mouse moves.  On very slow systems however, this flag may
00053 // still be useful.
00054 #ifndef FL_DOXYGEN
00055 
00056 #define CONSOLIDATE_MOTION 0
00057 extern "C" {
00058 #include <pthread.h>
00059 }
00060 
00061 #include <config.h>
00062 #include <FL/Fl.H>
00063 #include <FL/x.H>
00064 #include <FL/Fl_Window.H>
00065 #include <FL/Fl_Tooltip.H>
00066 #include <FL/Fl_Sys_Menu_Bar.H>
00067 #include <stdio.h>
00068 #include <stdlib.h>
00069 #include "flstring.h"
00070 #include <unistd.h>
00071 
00072 // #define DEBUG_SELECT         // UNCOMMENT FOR SELECT()/THREAD DEBUGGING
00073 #ifdef DEBUG_SELECT
00074 #include <stdio.h>              // testing
00075 #define DEBUGMSG(msg)           if ( msg ) fprintf(stderr, msg);
00076 #define DEBUGPERRORMSG(msg)     if ( msg ) perror(msg)
00077 #define DEBUGTEXT(txt)          txt
00078 #else
00079 #define DEBUGMSG(msg)
00080 #define DEBUGPERRORMSG(msg)
00081 #define DEBUGTEXT(txt)          NULL
00082 #endif /*DEBUG_SELECT*/
00083 
00084 // external functions
00085 extern Fl_Window* fl_find(Window);
00086 extern void fl_fix_focus();
00087 
00088 // forward definition of functions in this file
00089 static void handleUpdateEvent( WindowPtr xid );
00090 //+ int fl_handle(const EventRecord &event);
00091 static int FSSpec2UnixPath( FSSpec *fs, char *dst );
00092 // converting cr lf converter function
00093 static void convert_crlf(char * string, size_t len);
00094 
00095 // public variables
00096 int fl_screen;
00097 CGContextRef fl_gc = 0;
00098 Handle fl_system_menu;
00099 Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0;
00100 CursHandle fl_default_cursor;
00101 WindowRef fl_capture = 0;            // we need this to compensate for a missing(?) mouse capture
00102 ulong fl_event_time;                 // the last timestamp from an x event
00103 char fl_key_vector[32];              // used by Fl::get_key()
00104 bool fl_show_iconic;                 // true if called from iconize() - shows the next created window in collapsed state
00105 int fl_disable_transient_for;        // secret method of removing TRANSIENT_FOR
00106 const Fl_Window* fl_modal_for;       // parent of modal() window
00107 Fl_Region fl_window_region = 0;
00108 Window fl_window;
00109 Fl_Window *Fl_Window::current_;
00110 EventRef fl_os_event;           // last (mouse) event
00111 
00112 // forward declarations of variables in this file
00113 static int got_events = 0;
00114 static Fl_Window* resize_from_system;
00115 static CursPtr default_cursor_ptr;
00116 static Cursor default_cursor;
00117 static WindowRef fl_os_capture = 0; // the dispatch handler will redirect mose move and drag events to these windows
00118 
00119 #if CONSOLIDATE_MOTION
00120 static Fl_Window* send_motion;
00121 extern Fl_Window* fl_xmousewin;
00122 #endif
00123 
00124 enum { kEventClassFLTK = 'fltk' };
00125 enum { kEventFLTKBreakLoop = 1, kEventFLTKDataReady };
00126 
00127 /* fltk-utf8 placekeepers */
00128 void fl_reset_spot()
00129 {
00130 }
00131 
00132 void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
00133 {
00134 }
00135 
00136 void fl_set_status(int x, int y, int w, int h)
00137 {
00138 }
00139 
00143 static unsigned short macKeyLookUp[128] =
00144 {
00145     'a', 's', 'd', 'f', 'h', 'g', 'z', 'x',
00146     'c', 'v', '^', 'b', 'q', 'w', 'e', 'r',
00147 
00148     'y', 't', '1', '2', '3', '4', '6', '5',
00149     '=', '9', '7', '-', '8', '0', ']', 'o',
00150 
00151     'u', '[', 'i', 'p', FL_Enter, 'l', 'j', '\'',
00152     'k', ';', '\\', ',', '/', 'n', 'm', '.',
00153 
00154     FL_Tab, ' ', '`', FL_BackSpace, 
00155     FL_KP_Enter, FL_Escape, 0, 0/*FL_Meta_L*/,
00156     0/*FL_Shift_L*/, 0/*FL_Caps_Lock*/, 0/*FL_Alt_L*/, 0/*FL_Control_L*/, 
00157     0/*FL_Shift_R*/, 0/*FL_Alt_R*/, 0/*FL_Control_R*/, 0,
00158 
00159     0, FL_KP+'.', FL_Right, FL_KP+'*', 0, FL_KP+'+', FL_Left, FL_Delete,
00160     FL_Down, 0, 0, FL_KP+'/', FL_KP_Enter, FL_Up, FL_KP+'-', 0,
00161 
00162     0, FL_KP+'=', FL_KP+'0', FL_KP+'1', FL_KP+'2', FL_KP+'3', FL_KP+'4', FL_KP+'5',
00163     FL_KP+'6', FL_KP+'7', 0, FL_KP+'8', FL_KP+'9', 0, 0, 0,
00164 
00165     FL_F+5, FL_F+6, FL_F+7, FL_F+3, FL_F+8, FL_F+9, 0, FL_F+11,
00166     0, 0/*FL_F+13*/, FL_Print, FL_Scroll_Lock, 0, FL_F+10, FL_Menu, FL_F+12,
00167 
00168     0, FL_Pause, FL_Help, FL_Home, FL_Page_Up, FL_Delete, FL_F+4, FL_End,
00169     FL_F+2, FL_Page_Down, FL_F+1, FL_Left, FL_Right, FL_Down, FL_Up, 0/*FL_Power*/,
00170 };
00171 
00175 static unsigned int mods_to_e_state( UInt32 mods )
00176 {
00177   long state = 0;
00178   if ( mods & kEventKeyModifierNumLockMask ) state |= FL_NUM_LOCK;
00179   if ( mods & cmdKey ) state |= FL_META;
00180   if ( mods & (optionKey|rightOptionKey) ) state |= FL_ALT;
00181   if ( mods & (controlKey|rightControlKey) ) state |= FL_CTRL;
00182   if ( mods & (shiftKey|rightShiftKey) ) state |= FL_SHIFT;
00183   if ( mods & alphaLock ) state |= FL_CAPS_LOCK;
00184   unsigned int ret = ( Fl::e_state & 0xff000000 ) | state;
00185   Fl::e_state = ret;
00186   //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods );
00187   return ret;
00188 }
00189 
00190 
00194 static void mods_to_e_keysym( UInt32 mods )
00195 {
00196   if ( mods & cmdKey ) Fl::e_keysym = FL_Meta_L;
00197   else if ( mods & kEventKeyModifierNumLockMask ) Fl::e_keysym = FL_Num_Lock;
00198   else if ( mods & optionKey ) Fl::e_keysym = FL_Alt_L;
00199   else if ( mods & rightOptionKey ) Fl::e_keysym = FL_Alt_R;
00200   else if ( mods & controlKey ) Fl::e_keysym = FL_Control_L;
00201   else if ( mods & rightControlKey ) Fl::e_keysym = FL_Control_R;
00202   else if ( mods & shiftKey ) Fl::e_keysym = FL_Shift_L;
00203   else if ( mods & rightShiftKey ) Fl::e_keysym = FL_Shift_R;
00204   else if ( mods & alphaLock ) Fl::e_keysym = FL_Caps_Lock;
00205   else Fl::e_keysym = 0;
00206   //printf( "to sym 0x%08x (%04x)\n", Fl::e_keysym, mods );
00207 }
00208 // these pointers are set by the Fl::lock() function:
00209 static void nothing() {}
00210 void (*fl_lock_function)() = nothing;
00211 void (*fl_unlock_function)() = nothing;
00212 
00213 //
00214 // Select interface -- how it's implemented:
00215 //     When the user app configures one or more file descriptors to monitor
00216 //     with Fl::add_fd(), we start a separate thread to select() the  data,
00217 //     sending a custom OSX 'FLTK data ready event' to the parent  thread's
00218 //     RunApplicationLoop(), so that it triggers the data  ready  callbacks
00219 //     in the parent thread.                               -erco 04/04/04
00220 //     
00221 #define POLLIN  1
00222 #define POLLOUT 4
00223 #define POLLERR 8
00224 
00225 // Class to handle select() 'data ready'
00226 class DataReady
00227 {
00228     struct FD
00229     {
00230       int fd;
00231       short events;
00232       void (*cb)(int, void*);
00233       void* arg;
00234     };
00235     int nfds, fd_array_size;
00236     FD *fds;
00237     pthread_t tid;              // select()'s thread id
00238 
00239     // Data that needs to be locked (all start with '_')
00240     pthread_mutex_t _datalock;  // data lock
00241     fd_set _fdsets[3];          // r/w/x sets user wants to monitor
00242     int _maxfd;                 // max fd count to monitor
00243     int _cancelpipe[2];         // pipe used to help cancel thread
00244     void *_userdata;            // thread's userdata
00245 
00246 public:
00247     DataReady()
00248     {
00249       nfds = 0;
00250       fd_array_size = 0;
00251       fds = 0;
00252       tid = 0;
00253 
00254       pthread_mutex_init(&_datalock, NULL);
00255       FD_ZERO(&_fdsets[0]); FD_ZERO(&_fdsets[1]); FD_ZERO(&_fdsets[2]);
00256       _cancelpipe[0] = _cancelpipe[1] = 0;
00257       _userdata = 0;
00258       _maxfd = 0;
00259     }
00260 
00261     ~DataReady()
00262     {
00263         CancelThread(DEBUGTEXT("DESTRUCTOR\n"));
00264         if (fds) { free(fds); fds = 0; }
00265         nfds = 0;
00266     }
00267 
00268     // Locks
00269     //    The convention for locks: volatile vars start with '_',
00270     //    and must be locked before use. Locked code is prefixed 
00271     //    with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco
00272     //
00273     void DataLock() { pthread_mutex_lock(&_datalock); }
00274     void DataUnlock() { pthread_mutex_unlock(&_datalock); }
00275 
00276     // Accessors
00277     int IsThreadRunning() { return(tid ? 1 : 0); }
00278     int GetNfds() { return(nfds); }
00279     int GetCancelPipe(int ix) { return(_cancelpipe[ix]); }
00280     fd_set GetFdset(int ix) { return(_fdsets[ix]); }
00281 
00282     // Methods
00283     void AddFD(int n, int events, void (*cb)(int, void*), void *v);
00284     void RemoveFD(int n, int events);
00285     int CheckData(fd_set& r, fd_set& w, fd_set& x);
00286     void HandleData(fd_set& r, fd_set& w, fd_set& x);
00287     static void* DataReadyThread(void *self);
00288     void StartThread(void *userdata);
00289     void CancelThread(const char *reason);
00290 };
00291 
00292 static DataReady dataready;
00293 
00294 void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v)
00295 {
00296   RemoveFD(n, events);
00297   int i = nfds++;
00298   if (i >= fd_array_size) 
00299   {
00300     FD *temp;
00301     fd_array_size = 2*fd_array_size+1;
00302     if (!fds) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); }
00303     else { temp = (FD*)realloc(fds, fd_array_size*sizeof(FD)); }
00304     if (!temp) return;
00305     fds = temp;
00306   }
00307   fds[i].cb  = cb;
00308   fds[i].arg = v;
00309   fds[i].fd  = n;
00310   fds[i].events = events;
00311   DataLock();
00312   /*LOCK*/  if (events & POLLIN)  FD_SET(n, &_fdsets[0]);
00313   /*LOCK*/  if (events & POLLOUT) FD_SET(n, &_fdsets[1]);
00314   /*LOCK*/  if (events & POLLERR) FD_SET(n, &_fdsets[2]);
00315   /*LOCK*/  if (n > _maxfd) _maxfd = n;
00316   DataUnlock();
00317 }
00318 
00319 // Remove an FD from the array
00320 void DataReady::RemoveFD(int n, int events)
00321 {
00322   int i,j;
00323   for (i=j=0; i<nfds; i++)
00324   {
00325     if (fds[i].fd == n) 
00326     {
00327       int e = fds[i].events & ~events;
00328       if (!e) continue; // if no events left, delete this fd
00329       fds[i].events = e;
00330     }
00331     // move it down in the array if necessary:
00332     if (j<i)
00333       { fds[j] = fds[i]; }
00334     j++;
00335   }
00336   nfds = j;
00337   DataLock();
00338   /*LOCK*/  if (events & POLLIN)  FD_CLR(n, &_fdsets[0]);
00339   /*LOCK*/  if (events & POLLOUT) FD_CLR(n, &_fdsets[1]);
00340   /*LOCK*/  if (events & POLLERR) FD_CLR(n, &_fdsets[2]);
00341   /*LOCK*/  if (n == _maxfd) _maxfd--;
00342   DataUnlock();
00343 }
00344 
00345 // CHECK IF USER DATA READY, RETURNS r/w/x INDICATING WHICH IF ANY
00346 int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x)
00347 {
00348   int ret;
00349   DataLock();
00350   /*LOCK*/  timeval t = { 0, 1 };               // quick check
00351   /*LOCK*/  r = _fdsets[0], w = _fdsets[1], x = _fdsets[2];
00352   /*LOCK*/  ret = ::select(_maxfd+1, &r, &w, &x, &t);
00353   DataUnlock();
00354   if ( ret == -1 )
00355     { DEBUGPERRORMSG("CheckData(): select()"); }
00356   return(ret);
00357 }
00358 
00359 // HANDLE DATA READY CALLBACKS
00360 void DataReady::HandleData(fd_set& r, fd_set& w, fd_set& x)
00361 {
00362   for (int i=0; i<nfds; i++) 
00363   {
00364     int f = fds[i].fd;
00365     short revents = 0;
00366     if (FD_ISSET(f, &r)) revents |= POLLIN;
00367     if (FD_ISSET(f, &w)) revents |= POLLOUT;
00368     if (FD_ISSET(f, &x)) revents |= POLLERR;
00369     if (fds[i].events & revents) 
00370     {
00371       DEBUGMSG("DOING CALLBACK: ");
00372       fds[i].cb(f, fds[i].arg);
00373       DEBUGMSG("DONE\n");
00374     }
00375   }
00376 }
00377 
00378 // DATA READY THREAD
00379 //    This thread watches for changes in user's file descriptors.
00380 //    Sends a 'data ready event' to the main thread if any change.
00381 //
00382 void* DataReady::DataReadyThread(void *o)
00383 {
00384   DataReady *self = (DataReady*)o;
00385   while ( 1 )                                   // loop until thread cancel or error
00386   {
00387     // Thread safe local copies of data before each select()
00388     self->DataLock();
00389     /*LOCK*/  int maxfd = self->_maxfd;
00390     /*LOCK*/  fd_set r = self->GetFdset(0);
00391     /*LOCK*/  fd_set w = self->GetFdset(1);
00392     /*LOCK*/  fd_set x = self->GetFdset(2);
00393     /*LOCK*/  void *userdata = self->_userdata;
00394     /*LOCK*/  int cancelpipe = self->GetCancelPipe(0);
00395     /*LOCK*/  if ( cancelpipe > maxfd ) maxfd = cancelpipe;
00396     /*LOCK*/  FD_SET(cancelpipe, &r);           // add cancelpipe to fd's to watch
00397     /*LOCK*/  FD_SET(cancelpipe, &x);
00398     self->DataUnlock();
00399     // timeval t = { 1000, 0 }; // 1000 seconds;
00400     timeval t = { 2, 0 };       // HACK: 2 secs prevents 'hanging' problem
00401     int ret = ::select(maxfd+1, &r, &w, &x, &t);
00402     pthread_testcancel();       // OSX 10.0.4 and older: needed for parent to cancel
00403     switch ( ret )
00404     {
00405       case 0:   // NO DATA
00406         continue;
00407       case -1:  // ERROR
00408       {
00409         DEBUGPERRORMSG("CHILD THREAD: select() failed");
00410         return(NULL);           // error? exit thread
00411       }
00412       default:  // DATA READY
00413       {
00414         if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x))       // cancel?
00415             { return(NULL); }                                           // just exit
00416         DEBUGMSG("CHILD THREAD: DATA IS READY\n");
00417         EventRef drEvent;
00418         CreateEvent( 0, kEventClassFLTK, kEventFLTKDataReady,
00419                      0, kEventAttributeUserEvent, &drEvent);
00420         EventQueueRef eventqueue = (EventQueueRef)userdata;
00421         PostEventToQueue(eventqueue, drEvent, kEventPriorityStandard );
00422         ReleaseEvent( drEvent );
00423         return(NULL);           // done with thread
00424       }
00425     }
00426   }
00427 }
00428 
00429 // START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE
00430 void DataReady::StartThread(void *new_userdata)
00431 {
00432   CancelThread(DEBUGTEXT("STARTING NEW THREAD\n"));
00433   DataLock();
00434   /*LOCK*/  pipe(_cancelpipe);  // pipe for sending cancel msg to thread
00435   /*LOCK*/  _userdata = new_userdata;
00436   DataUnlock();
00437   DEBUGMSG("*** START THREAD\n");
00438   pthread_create(&tid, NULL, DataReadyThread, (void*)this);
00439 }
00440 
00441 // CANCEL 'DATA READY' THREAD, CLOSE PIPE
00442 void DataReady::CancelThread(const char *reason)
00443 {
00444   if ( tid )
00445   {
00446     DEBUGMSG("*** CANCEL THREAD: ");
00447     DEBUGMSG(reason);
00448     if ( pthread_cancel(tid) == 0 )             // cancel first
00449     {
00450       DataLock();
00451       /*LOCK*/  write(_cancelpipe[1], "x", 1);  // wake thread from select
00452       DataUnlock();
00453       pthread_join(tid, NULL);                  // wait for thread to finish
00454     }
00455     tid = 0;
00456     DEBUGMSG("(JOINED) OK\n");
00457   }
00458   // Close pipe if open
00459   DataLock();
00460   /*LOCK*/  if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; }
00461   /*LOCK*/  if ( _cancelpipe[1] ) { close(_cancelpipe[1]); _cancelpipe[1] = 0; }
00462   DataUnlock();
00463 }
00464 
00465 void Fl::add_fd( int n, int events, void (*cb)(int, void*), void *v )
00466     { dataready.AddFD(n, events, cb, v); }
00467 
00468 void Fl::add_fd(int fd, void (*cb)(int, void*), void* v)
00469     { dataready.AddFD(fd, POLLIN, cb, v); }
00470 
00471 void Fl::remove_fd(int n, int events)
00472     { dataready.RemoveFD(n, events); }
00473 
00474 void Fl::remove_fd(int n)
00475     { dataready.RemoveFD(n, -1); }
00476 
00480 int fl_ready()
00481 {
00482   EventRef event;
00483   return !ReceiveNextEvent(0, NULL, 0.0, false, &event);
00484 }
00485 
00490 OSStatus HandleMenu( HICommand *cmd )
00491 {
00492   OSStatus ret = eventNotHandledErr;
00493   // attributes, commandIDm menu.menuRef, menu.menuItemIndex
00494   UInt32 ref;
00495   OSErr rrc = GetMenuItemRefCon( cmd->menu.menuRef, cmd->menu.menuItemIndex, &ref );
00496   //printf( "%d, %08x, %08x, %d, %d, %8x\n", rrc, cmd->attributes, cmd->commandID, cmd->menu.menuRef, cmd->menu.menuItemIndex, rrc );
00497   if ( rrc==noErr && ref )
00498   {
00499     Fl_Menu_Item *m = (Fl_Menu_Item*)ref;
00500     //printf( "Menu: %s\n", m->label() );
00501     fl_sys_menu_bar->picked( m );
00502     if ( m->flags & FL_MENU_TOGGLE ) // update the menu toggle symbol
00503       SetItemMark( cmd->menu.menuRef, cmd->menu.menuItemIndex, (m->flags & FL_MENU_VALUE ) ? 0x12 : 0 );
00504     if ( m->flags & FL_MENU_RADIO ) // update all radio buttons in this menu
00505     {
00506       Fl_Menu_Item *j = m;
00507       int i = cmd->menu.menuItemIndex;
00508       for (;;)
00509       {
00510         if ( j->flags & FL_MENU_DIVIDER )
00511           break;
00512         j++; i++;
00513         if ( !j->text || !j->radio() )
00514           break;
00515         SetItemMark( cmd->menu.menuRef, i, ( j->flags & FL_MENU_VALUE ) ? 0x13 : 0 );
00516       }
00517       j = m-1; i = cmd->menu.menuItemIndex-1;
00518       for ( ; i>0; j--, i-- )
00519       {
00520         if ( !j->text || j->flags&FL_MENU_DIVIDER || !j->radio() )
00521           break;
00522         SetItemMark( cmd->menu.menuRef, i, ( j->flags & FL_MENU_VALUE ) ? 0x13 : 0 );
00523       }
00524       SetItemMark( cmd->menu.menuRef, cmd->menu.menuItemIndex, ( m->flags & FL_MENU_VALUE ) ? 0x13 : 0 );
00525     }
00526     ret = noErr; // done handling this event
00527   }
00528   HiliteMenu(0);
00529   return ret;
00530 }
00531 
00532 
00539 static pascal OSStatus carbonDispatchHandler( EventHandlerCallRef nextHandler, EventRef event, void *userData )
00540 {
00541   OSStatus ret = eventNotHandledErr;
00542   HICommand cmd;
00543 
00544   fl_lock_function();
00545 
00546   got_events = 1;
00547 
00548   switch ( GetEventClass( event ) )
00549   {
00550   case kEventClassMouse:
00551     switch ( GetEventKind( event ) )
00552     {
00553     case kEventMouseUp:
00554     case kEventMouseMoved:
00555     case kEventMouseDragged:
00556       if ( fl_capture )
00557         ret = SendEventToEventTarget( event, GetWindowEventTarget( fl_capture ) );
00558       else if ( fl_os_capture ){
00559         ret = SendEventToEventTarget( event, GetWindowEventTarget( fl_os_capture ) );
00560         fl_os_capture = 0;
00561       }
00562       break;
00563     }
00564     break;
00565   case kEventClassCommand:
00566     switch (GetEventKind( event ) )
00567     {
00568       case kEventCommandProcess:
00569         ret = GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd );
00570         if (ret == noErr && (cmd.attributes & kHICommandFromMenu) != 0) 
00571           ret = HandleMenu( &cmd );
00572         else 
00573           ret = eventNotHandledErr;
00574         break;
00575     }
00576     break;
00577   case kEventClassFLTK:
00578     switch ( GetEventKind( event ) )
00579     {
00580     case kEventFLTKBreakLoop:
00581       ret = noErr;
00582       break;
00583     case kEventFLTKDataReady:
00584       {
00585         dataready.CancelThread(DEBUGTEXT("DATA READY EVENT\n"));
00586 
00587         // CHILD THREAD TELLS US DATA READY
00588         //     Check to see what's ready, and invoke user's cb's
00589         //
00590         fd_set r,w,x;
00591         switch(dataready.CheckData(r,w,x))
00592         {
00593           case 0:       // NO DATA
00594             break;
00595           case -1:      // ERROR
00596             break;
00597           default:      // DATA READY
00598             dataready.HandleData(r,w,x);
00599             break;
00600         }
00601       }
00602       ret = noErr;
00603       break;
00604     }
00605   }
00606   if ( ret == eventNotHandledErr )
00607     ret = CallNextEventHandler( nextHandler, event ); // let the OS handle the activation, but continue to get a click-through effect
00608 
00609   fl_unlock_function();
00610 
00611   return ret;
00612 }
00613 
00614 
00618 static void breakMacEventLoop()
00619 {
00620   EventRef breakEvent;
00621 
00622   fl_lock_function();
00623 
00624   CreateEvent( 0, kEventClassFLTK, kEventFLTKBreakLoop, 0, kEventAttributeUserEvent, &breakEvent );
00625   PostEventToQueue( GetCurrentEventQueue(), breakEvent, kEventPriorityStandard );
00626   ReleaseEvent( breakEvent );
00627 
00628   fl_unlock_function();
00629 }
00630 
00631 //
00632 // MacOS X timers
00633 //
00634 
00635 struct MacTimeout {
00636     Fl_Timeout_Handler callback;
00637     void* data;
00638     EventLoopTimerRef timer;
00639     EventLoopTimerUPP upp;
00640     char pending; 
00641 };
00642 static MacTimeout* mac_timers;
00643 static int mac_timer_alloc;
00644 static int mac_timer_used;
00645 
00646 
00647 static void realloc_timers()
00648 {
00649     if (mac_timer_alloc == 0) {
00650         mac_timer_alloc = 8;
00651     }
00652     mac_timer_alloc *= 2;
00653     MacTimeout* new_timers = new MacTimeout[mac_timer_alloc];
00654     memset(new_timers, 0, sizeof(MacTimeout)*mac_timer_alloc);
00655     memcpy(new_timers, mac_timers, sizeof(MacTimeout) * mac_timer_used);
00656     MacTimeout* delete_me = mac_timers;
00657     mac_timers = new_timers;
00658     delete [] delete_me;
00659 }
00660 
00661 static void delete_timer(MacTimeout& t)
00662 {
00663     if (t.timer) {
00664         RemoveEventLoopTimer(t.timer);
00665         DisposeEventLoopTimerUPP(t.upp);
00666         memset(&t, 0, sizeof(MacTimeout));
00667     }
00668 }
00669 
00670 
00671 static pascal void do_timer(EventLoopTimerRef timer, void* data)
00672 {
00673    for (int i = 0;  i < mac_timer_used;  ++i) {
00674         MacTimeout& t = mac_timers[i];
00675         if (t.timer == timer  &&  t.data == data) {
00676             t.pending = 0;
00677             (*t.callback)(data);
00678             if (t.pending==0)
00679               delete_timer(t);
00680             break;
00681         }
00682     }
00683     breakMacEventLoop();
00684 }
00685 
00691 static double do_queued_events( double time = 0.0 ) 
00692 {
00693   static bool been_here = false;
00694   static RgnHandle rgn;
00695   
00696   // initialize events and a region that enables mouse move events
00697   if (!been_here) {
00698     rgn = NewRgn();
00699     Point mp;
00700     GetMouse(&mp);
00701     SetRectRgn(rgn, mp.h, mp.v, mp.h, mp.v);
00702     SetEventMask(everyEvent);
00703     been_here = true;
00704   }
00705   OSStatus ret;
00706   static EventTargetRef target = 0;
00707   if ( !target ) 
00708   {
00709     target = GetEventDispatcherTarget();
00710 
00711     EventHandlerUPP dispatchHandler = NewEventHandlerUPP( carbonDispatchHandler ); // will not be disposed by Carbon...
00712     static EventTypeSpec dispatchEvents[] = {
00713         { kEventClassWindow, kEventWindowShown },
00714         { kEventClassWindow, kEventWindowHidden },
00715         { kEventClassWindow, kEventWindowActivated },
00716         { kEventClassWindow, kEventWindowDeactivated },
00717         { kEventClassWindow, kEventWindowClose },
00718         { kEventClassKeyboard, kEventRawKeyDown },
00719         { kEventClassKeyboard, kEventRawKeyRepeat },
00720         { kEventClassKeyboard, kEventRawKeyUp },
00721         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
00722         { kEventClassMouse, kEventMouseDown },
00723         { kEventClassMouse, kEventMouseUp },
00724         { kEventClassMouse, kEventMouseMoved },
00725         { kEventClassMouse, 11 }, // MightyMouse wheels
00726         { kEventClassMouse, kEventMouseWheelMoved },
00727         { kEventClassMouse, kEventMouseDragged },
00728         { kEventClassFLTK, kEventFLTKBreakLoop },
00729         { kEventClassFLTK, kEventFLTKDataReady } };
00730     ret = InstallEventHandler( target, dispatchHandler, GetEventTypeCount(dispatchEvents), dispatchEvents, 0, 0L );
00731     static EventTypeSpec appEvents[] = {
00732         { kEventClassCommand, kEventCommandProcess } };
00733     ret = InstallApplicationEventHandler( dispatchHandler, GetEventTypeCount(appEvents), appEvents, 0, 0L );
00734   }
00735 
00736   got_events = 0;
00737 
00738   // Check for re-entrant condition
00739   if ( dataready.IsThreadRunning() )
00740     { dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); }
00741 
00742   // Start thread to watch for data ready
00743   if ( dataready.GetNfds() )
00744       { dataready.StartThread((void*)GetCurrentEventQueue()); }
00745 
00746   fl_unlock_function();
00747 
00748   EventRef event;
00749   EventTimeout timeout = time;
00750   if (!ReceiveNextEvent(0, NULL, timeout, true, &event)) {
00751     got_events = 1;
00752     OSErr ret = SendEventToEventTarget( event, target );
00753     if (ret!=noErr) {
00754       EventRecord clevent;
00755       ConvertEventRefToEventRecord(event, &clevent);
00756       if (clevent.what==kHighLevelEvent) {
00757         ret = AEProcessAppleEvent(&clevent);
00758       }
00759     }
00760     if (   ret==eventNotHandledErr
00761         && GetEventClass(event)==kEventClassMouse
00762         && GetEventKind(event)==kEventMouseDown ) {
00763       WindowRef win; Point pos;
00764       GetEventParameter(event, kEventParamMouseLocation, typeQDPoint,
00765         NULL, sizeof(pos), NULL, &pos);
00766       if (MacFindWindow(pos, &win)==inMenuBar) {
00767         MenuSelect(pos);
00768       }
00769     }
00770     ReleaseEvent( event );
00771   }
00772 
00773   fl_lock_function();
00774 
00775 #if CONSOLIDATE_MOTION
00776   if (send_motion && send_motion == fl_xmousewin) {
00777     send_motion = 0;
00778     Fl::handle(FL_MOVE, fl_xmousewin);
00779   }
00780 #endif
00781 
00782   return time;
00783 }
00784 
00785 
00793 int fl_wait( double time ) 
00794 {
00795   do_queued_events( time );
00796   return (got_events);
00797 }
00798 
00799 
00804 static OSErr QuitAppleEventHandler( const AppleEvent *appleEvt, AppleEvent* reply, UInt32 refcon )
00805 {
00806   fl_lock_function();
00807 
00808   while ( Fl_X::first ) {
00809     Fl_X *x = Fl_X::first;
00810     Fl::handle( FL_CLOSE, x->w );
00811     if ( Fl_X::first == x ) {
00812       fl_unlock_function();
00813       return noErr; // FLTK has not close all windows, so we return to the main program now
00814     }
00815   }
00816 
00817   fl_unlock_function();
00818 
00819   return noErr;
00820 }
00821 
00822 
00827 static pascal OSStatus carbonWindowHandler( EventHandlerCallRef nextHandler, EventRef event, void *userData )
00828 {
00829   UInt32 kind = GetEventKind( event );
00830   OSStatus ret = eventNotHandledErr;
00831   Fl_Window *window = (Fl_Window*)userData;
00832   Fl::first_window(window);
00833 
00834   Rect currentBounds, originalBounds;
00835   WindowClass winClass;
00836   static Fl_Window *activeWindow = 0;
00837   
00838   fl_lock_function();
00839   
00840   switch ( kind )
00841   {
00842   case kEventWindowBoundsChanging:
00843     GetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &currentBounds );
00844     GetEventParameter( event, kEventParamOriginalBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &originalBounds );
00845     break;
00846   case kEventWindowDrawContent:
00847     handleUpdateEvent( fl_xid( window ) );
00848     ret = noErr;
00849     break;
00850   case kEventWindowBoundsChanged: {
00851     GetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &currentBounds );
00852     GetEventParameter( event, kEventParamOriginalBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &originalBounds );
00853     int X = currentBounds.left, W = currentBounds.right-X;
00854     int Y = currentBounds.top, H = currentBounds.bottom-Y;
00855     resize_from_system = window;
00856     window->resize( X, Y, W, H );
00857     if ( ( originalBounds.right - originalBounds.left != W ) 
00858       || ( originalBounds.bottom - originalBounds.top != H ) )
00859     {
00860       if ( window->shown() ) 
00861         handleUpdateEvent( fl_xid( window ) );
00862     } 
00863     break; }
00864   case kEventWindowShown:
00865     if ( !window->parent() )
00866     {
00867       GetWindowClass( fl_xid( window ), &winClass );
00868       if ( winClass != kHelpWindowClass ) {     // help windows can't get the focus!
00869         Fl::handle( FL_FOCUS, window);
00870         activeWindow = window;
00871       }
00872       Fl::handle( FL_SHOW, window);
00873       mods_to_e_state(GetCurrentKeyModifiers());
00874     }
00875     break;
00876   case kEventWindowHidden:
00877     if ( !window->parent() ) Fl::handle( FL_HIDE, window);
00878     break;
00879   case kEventWindowActivated:
00880     if ( window->shown() && window!=activeWindow )
00881     {
00882       GetWindowClass( fl_xid( window ), &winClass );
00883       if ( winClass != kHelpWindowClass ) {     // help windows can't get the focus!
00884         Fl::handle( FL_FOCUS, window);
00885         activeWindow = window;
00886       }
00887     }
00888     break;
00889   case kEventWindowDeactivated:
00890     if ( window==activeWindow ) 
00891     {
00892       Fl::handle( FL_UNFOCUS, window);
00893       activeWindow = 0;
00894     }
00895     break;
00896   case kEventWindowClose:
00897     Fl::handle( FL_CLOSE, window ); // this might or might not close the window
00898     // if there are no more windows, send a high-level quit event
00899     if (!Fl_X::first) QuitAppleEventHandler( 0, 0, 0 );
00900     ret = noErr; // returning noErr tells Carbon to stop following up on this event
00901     break;
00902   case kEventWindowCollapsed:
00903     window->clear_visible();
00904     break;
00905   case kEventWindowExpanded:
00906     window->set_visible();
00907     break;
00908   }
00909 
00910   fl_unlock_function();
00911 
00912   return ret;
00913 }
00914 
00915 
00920 static pascal OSStatus carbonMousewheelHandler( EventHandlerCallRef nextHandler, EventRef event, void *userData )
00921 {
00922   // Handle the new "MightyMouse" mouse wheel events. Please, someone explain
00923   // to me why Apple changed the API on this even though the current API
00924   // supports two wheels just fine. Matthias,
00925   fl_lock_function();
00926 
00927   fl_os_event = event;
00928   Fl_Window *window = (Fl_Window*)userData;
00929   if ( !window->shown() )
00930   {
00931     fl_unlock_function();
00932     return noErr;
00933   }
00934   Fl::first_window(window);
00935 
00936   EventMouseWheelAxis axis;
00937   GetEventParameter( event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(EventMouseWheelAxis), NULL, &axis );
00938   long delta;
00939   GetEventParameter( event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(long), NULL, &delta );
00940 //  fprintf(stderr, "axis=%d, delta=%d\n", axis, delta);
00941   if ( axis == kEventMouseWheelAxisX ) {
00942     Fl::e_dx = -delta;
00943     Fl::e_dy = 0;
00944     if ( Fl::e_dx) Fl::handle( FL_MOUSEWHEEL, window );
00945   } else if ( axis == kEventMouseWheelAxisY ) {
00946     Fl::e_dx = 0;
00947     Fl::e_dy = -delta;
00948     if ( Fl::e_dy) Fl::handle( FL_MOUSEWHEEL, window );
00949   } else {
00950     fl_unlock_function();
00951 
00952     return eventNotHandledErr;
00953   }
00954 
00955   fl_unlock_function();
00956   
00957   return noErr;
00958 }
00959 
00960 
00964 static void chord_to_e_state( UInt32 chord )
00965 {
00966   static ulong state[] = 
00967   { 
00968     0, FL_BUTTON1, FL_BUTTON3, FL_BUTTON1|FL_BUTTON3, FL_BUTTON2,
00969     FL_BUTTON2|FL_BUTTON1, FL_BUTTON2|FL_BUTTON3, 
00970     FL_BUTTON2|FL_BUTTON1|FL_BUTTON3
00971   };
00972   Fl::e_state = ( Fl::e_state & 0xff0000 ) | state[ chord & 0x07 ];
00973 }
00974 
00975 
00979 static pascal OSStatus carbonMouseHandler( EventHandlerCallRef nextHandler, EventRef event, void *userData )
00980 {
00981   static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2 };
00982   static int px, py;
00983   static char suppressed = 0;
00984 
00985   fl_lock_function();
00986   
00987   fl_os_event = event;
00988   Fl_Window *window = (Fl_Window*)userData;
00989   if ( !window->shown() )
00990   {
00991     fl_unlock_function();
00992     return noErr;
00993   }
00994   Fl::first_window(window);
00995   Point pos;
00996   GetEventParameter( event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &pos );
00997   EventMouseButton btn;
00998   GetEventParameter( event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &btn );
00999   UInt32 clickCount;
01000   GetEventParameter( event, kEventParamClickCount, typeUInt32, NULL, sizeof(UInt32), NULL, &clickCount );
01001   UInt32 chord;
01002   GetEventParameter( event, kEventParamMouseChord, typeUInt32, NULL, sizeof(UInt32), NULL, &chord );
01003   WindowRef xid = fl_xid(window), tempXid;
01004   int sendEvent = 0, part = 0;
01005   switch ( GetEventKind( event ) )
01006   {
01007   case kEventMouseDown:
01008     part = FindWindow( pos, &tempXid );
01009     if (!(Fl::grab() && window!=Fl::grab())) {
01010       if ( part == inGrow ) {
01011         fl_unlock_function();
01012         suppressed = 1;
01013         Fl_Tooltip::current(0L);
01014         return CallNextEventHandler( nextHandler, event ); // let the OS handle this for us
01015       }
01016       if ( part != inContent ) {
01017         fl_unlock_function();
01018         suppressed = 1;
01019         Fl_Tooltip::current(0L);
01020         // anything else to here?
01021         return CallNextEventHandler( nextHandler, event ); // let the OS handle this for us
01022       }
01023     }
01024     suppressed = 0;
01025     if (part==inContent && !IsWindowActive( xid ) ) {
01026       CallNextEventHandler( nextHandler, event ); // let the OS handle the activation, but continue to get a click-through effect
01027     }
01028     // normal handling of mouse-down follows
01029     fl_os_capture = xid;
01030     sendEvent = FL_PUSH;
01031     Fl::e_is_click = 1; px = pos.h; py = pos.v;
01032     if (clickCount>1) 
01033       Fl::e_clicks++;
01034     else
01035       Fl::e_clicks = 0;
01036     // fall through
01037   case kEventMouseUp:
01038     if (suppressed) {
01039       suppressed = 0;
01040       break;
01041     }
01042     if ( !window ) break;
01043     if ( !sendEvent ) {
01044       sendEvent = FL_RELEASE; 
01045     }
01046     Fl::e_keysym = keysym[ btn ];
01047     // fall through
01048   case kEventMouseMoved:
01049     suppressed = 0;
01050     if ( !sendEvent ) { 
01051       sendEvent = FL_MOVE; chord = 0; 
01052     }
01053     // fall through
01054   case kEventMouseDragged:
01055     if (suppressed) break;
01056     if ( !sendEvent ) {
01057       sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG
01058       if (abs(pos.h-px)>5 || abs(pos.v-py)>5) 
01059         Fl::e_is_click = 0;
01060     }
01061     chord_to_e_state( chord );
01062     GrafPtr oldPort;
01063     GetPort( &oldPort );
01064     SetPort( GetWindowPort(xid) ); // \todo replace this! There must be some GlobalToLocal call that has a port as an argument
01065     SetOrigin(0, 0);
01066     Fl::e_x_root = pos.h;
01067     Fl::e_y_root = pos.v;
01068     GlobalToLocal( &pos );
01069     Fl::e_x = pos.h;
01070     Fl::e_y = pos.v;
01071     SetPort( oldPort );
01072     if (GetEventKind(event)==kEventMouseDown && part!=inContent) {
01073       int used = Fl::handle( sendEvent, window );
01074       CallNextEventHandler( nextHandler, event ); // let the OS handle this for us
01075       if (!used) 
01076         suppressed = 1;
01077     } else {
01078       Fl::handle( sendEvent, window );
01079     }
01080     break;
01081   }
01082 
01083   fl_unlock_function();
01084   
01085   return noErr;
01086 }
01087 
01088 
01092 static unsigned short keycode_to_sym( UInt32 keyCode, UInt32 mods, unsigned short deflt )
01093 {
01094   static Ptr map = 0;
01095   UInt32 state = 0;
01096   if (!map) {
01097     map = (Ptr)GetScriptManagerVariable(smKCHRCache);
01098     if (!map) {
01099       long kbID = GetScriptManagerVariable(smKeyScript);
01100       map = *GetResource('KCHR', kbID);
01101     }
01102   }
01103   if (map)
01104     return KeyTranslate(map, keyCode|mods, &state );
01105   return deflt;
01106 }
01107 
01108 /*
01109  * keycode_function for post-10.5 systems, allows more sophisticated decoding of keys
01110  */
01111 static int keycodeToUnicode(
01112                 char * uniChars, int maxChars,
01113                 EventKind eKind,
01114                 UInt32 keycode, UInt32 modifiers,
01115                 UInt32 * deadKeyStatePtr,
01116                 unsigned char,  // not used in this function
01117                 unsigned short) // not used in this function
01118 {
01119   // first get the keyboard mapping in a post 10.2 way
01120   
01121   Ptr resource;
01122   TextEncoding encoding;
01123   static TextEncoding lastEncoding = kTextEncodingMacRoman;
01124   int len = 0;
01125   KeyboardLayoutRef currentLayout = NULL;
01126   static KeyboardLayoutRef lastLayout = NULL;
01127   SInt32 currentLayoutId = 0;
01128   static SInt32 lastLayoutId;
01129   int hasLayoutChanged = false;
01130   static Ptr uchr = NULL;
01131   static Ptr KCHR = NULL;
01132   // ScriptCode currentKeyScript;
01133   
01134   KLGetCurrentKeyboardLayout(&currentLayout);
01135   if (currentLayout) {
01136     KLGetKeyboardLayoutProperty(currentLayout, kKLIdentifier, (const void**)&currentLayoutId);
01137     if ( (lastLayout != currentLayout) || (lastLayoutId != currentLayoutId) ) {
01138       lastLayout = currentLayout;
01139       lastLayoutId = currentLayoutId;
01140       uchr = NULL;
01141       KCHR = NULL;
01142       if ((KLGetKeyboardLayoutProperty(currentLayout, kKLuchrData, (const void**)&uchr) == noErr) && (uchr != NULL)) {
01143         // done
01144       } else if ((KLGetKeyboardLayoutProperty(currentLayout, kKLKCHRData, (const void**)&KCHR) == noErr) && (KCHR != NULL)) {
01145         // done
01146       }
01147       // FIXME No Layout property found. Now we have a problem. 
01148     }
01149   }
01150   if (hasLayoutChanged) {
01151     //deadKeyStateUp = deadKeyStateDown = 0;
01152     if (KCHR != NULL) {
01153       // FIXME this must not happen
01154     } else if (uchr == NULL) {
01155       KCHR = (Ptr) GetScriptManagerVariable(smKCHRCache);
01156     }
01157   }
01158   if (uchr != NULL) {
01159     // this is what I expect
01160     resource = uchr;
01161   } else {
01162     resource = KCHR;
01163     encoding = lastEncoding;
01164     // this is actually not supported by the following code and will likely crash
01165   }
01166   
01167   // now apply that keyboard mapping to our keycode
01168   
01169   int action;
01170   //OptionBits options = 0;
01171   // not used yet: OptionBits options = kUCKeyTranslateNoDeadKeysMask;
01172   unsigned long keyboardType;
01173   keycode &= 0xFF;
01174   modifiers = (modifiers >> 8) & 0xFF;
01175   keyboardType = LMGetKbdType();
01176   OSStatus status;
01177   UniCharCount actuallength;
01178   UniChar utext[10];
01179   
01180   switch(eKind) {     
01181     case kEventRawKeyDown:    action = kUCKeyActionDown; break;
01182     case kEventRawKeyUp:      action = kUCKeyActionUp; break;
01183     case kEventRawKeyRepeat:  action = kUCKeyActionAutoKey; break;
01184     default: return 0;
01185   }
01186 
01187   UInt32 deadKeyState = *deadKeyStatePtr;
01188   if ((action==kUCKeyActionUp)&&(*deadKeyStatePtr))
01189     deadKeyStatePtr = &deadKeyState;
01190 
01191   status = UCKeyTranslate(
01192                           (const UCKeyboardLayout *) uchr,
01193                           keycode, action, modifiers, keyboardType,
01194                           0, deadKeyStatePtr,
01195                           10, &actuallength, utext);
01196 
01197   if (noErr != status) {
01198     fprintf(stderr,"UCKeyTranslate failed: %d\n", (int) status);
01199     actuallength = 0;
01200   }
01201 
01202   // convert the list of unicode chars into utf8
01203   // FIXME no bounds check (see maxchars)
01204   unsigned i;
01205   for (i=0; i<actuallength; ++i) {
01206     len += fl_utf8encode(utext[i], uniChars+len);
01207   }
01208   uniChars[len] = 0;
01209   return len;
01210 }
01211 
01212 /*
01213  * keycode_function for pre-10.5 systems, this is the "historic" fltk Mac key handling
01214  */
01215 static int keycode_wrap_old(
01216                 char * buffer,
01217                 int, EventKind, UInt32, // not used in this function
01218                 UInt32, UInt32 *,       // not used in this function
01219                 unsigned char key,
01220                 unsigned short sym)
01221 {
01222   if ( (sym >= FL_KP && sym <= FL_KP_Last) || !(sym & 0xff00) ||
01223         sym == FL_Tab || sym == FL_Enter) {
01224     buffer[0] = key;
01225     return 1;
01226   } else {
01227     buffer[0] = 0;
01228     return 0;
01229   }
01230 } /* keycode_wrap_old */
01231 /* 
01232  * Stub pointer to select appropriate keycode_function per operating system version. This function pointer
01233  * is initialised in fl_open_display, based on the runtime identification of the host OS version. This is
01234  * intended to allow us to utilise 10.5 services dynamically to improve Unicode handling, whilst still 
01235  * allowing code to run satisfactorily on older systems.
01236  */
01237 static int (*keycode_function)(char*, int, EventKind, UInt32, UInt32, UInt32*, unsigned char, unsigned short) = keycode_wrap_old;
01238 
01239 
01240 // EXPERIMENTAL!
01241 pascal OSStatus carbonTextHandler( 
01242   EventHandlerCallRef nextHandler, EventRef event, void *userData )
01243 {
01244   Fl_Window *window = (Fl_Window*)userData;
01245   Fl::first_window(window);
01246   fl_lock_function();
01247   //int kind = GetEventKind(event);
01248   unsigned short buf[200];
01249   ByteCount size;
01250   GetEventParameter( event, kEventParamTextInputSendText, typeUnicodeText, 
01251                      NULL, 100, &size, &buf );
01252 //  printf("TextEvent: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);
01253   // FIXME: oversimplified!
01254   unsigned ucs = buf[0];
01255   char utf8buf[20];
01256   int len = fl_utf8encode(ucs, utf8buf);
01257   Fl::e_length = len;
01258   Fl::e_text = utf8buf;
01259   while (window->parent()) window = window->window();
01260   Fl::handle(FL_KEYBOARD, window);
01261   fl_unlock_function();
01262   fl_lock_function();
01263   Fl::handle(FL_KEYUP, window);
01264   fl_unlock_function();
01265   // for some reason, the window does not redraw until the next mouse move or button push
01266   // sending a 'redraw()' or 'awake()' does not solve the issue!
01267   Fl::flush();
01268   return noErr;
01269 }  
01270 
01274 pascal OSStatus carbonKeyboardHandler( 
01275   EventHandlerCallRef nextHandler, EventRef event, void *userData )
01276 {
01277   static char buffer[32];
01278   int sendEvent = 0;
01279   Fl_Window *window = (Fl_Window*)userData;
01280   Fl::first_window(window);
01281   UInt32 mods;
01282   static UInt32 prevMods = mods_to_e_state( GetCurrentKeyModifiers() );
01283 
01284   fl_lock_function();
01285   
01286   int kind = GetEventKind(event);
01287   
01288   // get the modifiers for any of the events
01289   GetEventParameter( event, kEventParamKeyModifiers, typeUInt32, 
01290                      NULL, sizeof(UInt32), NULL, &mods );
01291   
01292   // get the key code only for key events
01293   UInt32 keyCode = 0, maskedKeyCode = 0;
01294   unsigned char key = 0;
01295   unsigned short sym = 0;
01296   if (kind!=kEventRawKeyModifiersChanged) {
01297     GetEventParameter( event, kEventParamKeyCode, typeUInt32, 
01298                        NULL, sizeof(UInt32), NULL, &keyCode );
01299     GetEventParameter( event, kEventParamKeyMacCharCodes, typeChar, 
01300                        NULL, sizeof(char), NULL, &key );
01301   }
01302   // extended keyboards can also send sequences on key-up to generate Kanji etc. codes.
01303   // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode.
01304   // In this mode, there seem to be no key-down codes
01305 // printf("%08x %08x %08x\n", keyCode, mods, key);
01306   maskedKeyCode = keyCode & 0x7f;
01307   /* output a human readable event identifier for debugging 
01308   const char *ev = "";
01309   switch (kind) {
01310     case kEventRawKeyDown: ev = "kEventRawKeyDown"; break;
01311     case kEventRawKeyRepeat: ev = "kEventRawKeyRepeat"; break;
01312     case kEventRawKeyUp: ev = "kEventRawKeyUp"; break;
01313     case kEventRawKeyModifiersChanged: ev = "kEventRawKeyModifiersChanged"; break;
01314     default: ev = "unknown";
01315   }
01316   printf("%08x %08x %08x '%c' %s \n", mods, keyCode, key, key, ev);
01317   */
01318   switch (kind)
01319   {
01320   case kEventRawKeyDown:
01321   case kEventRawKeyRepeat:
01322 /*
01323     // FIXME Matt: For 10.5, the keycode_function will handle all this. This is untested for ealier versions of OS X.
01324     // When the user presses a "dead key", no information is send about
01325     // which dead key symbol was created. So we need to trick Carbon into
01326     // giving us the code by sending a "space" after the "dead key".
01327     if (key==0) {
01328       UInt32 ktState = 0;
01329       KeyboardLayoutRef klr;
01330       KLGetCurrentKeyboardLayout(&klr);
01331       const void *kchar = 0; KLGetKeyboardLayoutProperty(klr, kKLKCHRData, &kchar);
01332       KeyTranslate(kchar, (mods&0xff00) | keyCode, &ktState); // send the dead key
01333       key = KeyTranslate(kchar, 0x31, &ktState); // fake a space key press
01334       Fl::e_state |= 0x40000000; // mark this as a dead key
01335     } else {
01336       Fl::e_state &= 0xbfffffff; // clear the deadkey flag
01337     }
01338 */
01339     sendEvent = FL_KEYBOARD;
01340     // fall through
01341   case kEventRawKeyUp:
01342     if ( !sendEvent ) {
01343       sendEvent = FL_KEYUP;
01344       Fl::e_state &= 0xbfffffff; // clear the deadkey flag
01345     }
01346     // if the user pressed alt/option, event_key should have the keycap, 
01347     // but event_text should generate the international symbol
01348     sym = macKeyLookUp[maskedKeyCode];
01349     if ( isalpha(key) )
01350       sym = tolower(key);
01351     else if ( Fl::e_state&FL_CTRL && key<32 && sym<0xff00)
01352       sym = key+96;
01353     else if ( Fl::e_state&FL_ALT && sym<0xff00) // find the keycap of this key
01354       sym = keycode_to_sym( maskedKeyCode, 0, macKeyLookUp[ maskedKeyCode ] );
01355     Fl::e_keysym = Fl::e_original_keysym = sym;
01356     // Handle FL_KP_Enter on regular keyboards and on Powerbooks
01357     if ( maskedKeyCode==0x4c || maskedKeyCode==0x34) key=0x0d;    
01358     // Handle the Delete key on the keypad
01359     // Matt: the Mac has no concept of a NumLock key, or at least not visible
01360     // Matt: to Carbon. The kEventKeyModifierNumLockMask is only set when
01361     // Matt: a numeric keypad key is pressed and does not correspond with
01362     // Matt: the NumLock light in PowerBook keyboards.
01363 
01364     // Matt: attempt to get the correct Unicode character(s) from our keycode
01365     // imm:  keycode_function function pointer added to allow us to use different functions
01366     // imm:  depending on which OS version we are running on (tested and set in fl_open_display)
01367     static UInt32 deadKeyState = 0; // must be cleared when losing focus
01368     Fl::e_length = (*keycode_function)(buffer, 31, kind, keyCode, mods, &deadKeyState, key, sym);
01369     Fl::e_text = buffer;
01370     buffer[Fl::e_length] = 0; // just in case...
01371     break;
01372   case kEventRawKeyModifiersChanged: {
01373     UInt32 tMods = prevMods ^ mods;
01374     if ( tMods )
01375     {
01376       mods_to_e_keysym( tMods );
01377       if ( Fl::e_keysym ) 
01378         sendEvent = ( prevMods<mods ) ? FL_KEYBOARD : FL_KEYUP;
01379       Fl::e_length = 0;
01380       buffer[0] = 0;
01381       prevMods = mods;
01382     }
01383     mods_to_e_state( mods );
01384     break; }
01385   }
01386   while (window->parent()) window = window->window();
01387   if (sendEvent && Fl::handle(sendEvent,window)) {
01388     fl_unlock_function();  
01389     return noErr; // return noErr if FLTK handled the event
01390   } else {
01391     fl_unlock_function();
01392     //return CallNextEventHandler( nextHandler, event );;
01393     // Matt: I had better results (no duplicate events) always returning
01394     // Matt: 'noErr'. System keyboard events still seem to work just fine.
01395     return noErr;
01396   }
01397 }
01398 
01399 
01400 
01405 static void     (*open_cb)(const char *) = 0;
01406 
01407 
01413 static OSErr OpenAppleEventHandler(const AppleEvent *appleEvt,
01414                                    AppleEvent *reply,
01415                                    UInt32 refcon) {
01416   OSErr err;
01417   AEDescList documents;
01418   long i, n;
01419   FSSpec fileSpec;
01420   AEKeyword keyWd;
01421   DescType typeCd;
01422   Size actSz;
01423   char filename[1024];
01424 
01425   if (!open_cb) return noErr;
01426 
01427   // Initialize the document list...
01428   AECreateDesc(typeNull, NULL, 0, &documents);
01429  
01430   // Get the open parameter(s)...
01431   err = AEGetParamDesc(appleEvt, keyDirectObject, typeAEList, &documents);
01432   if (err != noErr) {
01433     AEDisposeDesc(&documents);
01434     return err;
01435   }
01436 
01437   // Lock access to FLTK in this thread...
01438   fl_lock_function();
01439 
01440   // Open the documents via the callback...
01441   if (AECountItems(&documents, &n) == noErr) {
01442     for (i = 1; i <= n; i ++) {
01443       // Get the next FSSpec record...
01444       AEGetNthPtr(&documents, i, typeFSS, &keyWd, &typeCd,
01445                   (Ptr)&fileSpec, sizeof(fileSpec),
01446                   (actSz = sizeof(fileSpec), &actSz));
01447 
01448       // Convert to a UNIX path...
01449       FSSpec2UnixPath(&fileSpec, filename);
01450 
01451       // Call the callback with the filename...
01452       (*open_cb)(filename);
01453     }
01454   }
01455 
01456   // Unlock access to FLTK for all threads...
01457   fl_unlock_function();
01458 
01459   // Get rid of the document list...
01460   AEDisposeDesc(&documents);
01461 
01462   return noErr;
01463 }
01464 
01465 
01470 void fl_open_callback(void (*cb)(const char *)) {
01471   open_cb = cb;
01472   if (cb) {
01473     AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
01474                           NewAEEventHandlerUPP((AEEventHandlerProcPtr)
01475                               OpenAppleEventHandler), 0, false);
01476   } else {
01477     AERemoveEventHandler(kCoreEventClass, kAEOpenDocuments,
01478                           NewAEEventHandlerUPP((AEEventHandlerProcPtr)
01479                               OpenAppleEventHandler), false);
01480   }
01481 }
01482 
01483 
01488 extern "C" {
01489   extern OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn, UInt32 _arg2,
01490     UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
01491 }
01492 
01493 void fl_open_display() {
01494   static char beenHereDoneThat = 0;
01495   if ( !beenHereDoneThat )  {
01496     beenHereDoneThat = 1;
01497     
01498     FlushEvents(everyEvent,0);
01499     
01500     MoreMasters(); // \todo Carbon suggests MoreMasterPointers()
01501     AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP((AEEventHandlerProcPtr)QuitAppleEventHandler), 0, false );
01502     
01503     // create the Mac Handle for the default cursor (a pointer to a pointer)
01504     GetQDGlobalsArrow(&default_cursor);
01505     default_cursor_ptr = &default_cursor;
01506     fl_default_cursor  = &default_cursor_ptr;
01507     
01508     ClearMenuBar();
01509     AppendResMenu( GetMenuHandle( 1 ), 'DRVR' );
01510     DrawMenuBar();
01511     
01512     // bring the application into foreground without a 'CARB' resource
01513     Boolean same_psn;
01514     ProcessSerialNumber cur_psn, front_psn;
01515     if( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) &&
01516        !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn )
01517     {
01518       // only transform the application type for unbundled apps
01519       CFBundleRef bundle = CFBundleGetMainBundle();
01520       if( bundle )
01521       {
01522         FSRef execFs;
01523         CFURLRef execUrl = CFBundleCopyExecutableURL( bundle );
01524         CFURLGetFSRef( execUrl, &execFs );
01525         
01526         FSRef bundleFs;
01527         GetProcessBundleLocation( &cur_psn, &bundleFs );
01528         
01529         if( !FSCompareFSRefs( &execFs, &bundleFs ) )
01530           bundle = NULL;
01531         
01532         CFRelease(execUrl);
01533       }
01534       
01535       if( !bundle )
01536       {
01537         // Earlier versions of this code tried to use weak linking, however it
01538         // appears that this does not work on 10.2.  Since 10.3 and higher provide
01539         // both TransformProcessType and CPSEnableForegroundOperation, the following
01540         // conditional code compiled on 10.2 will still work on newer releases...
01541         OSErr err;
01542         
01543 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01544         if (TransformProcessType != NULL) {
01545           err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication);
01546         } else
01547 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01548           err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103);
01549         
01550         if (err == noErr) {
01551           SetFrontProcess( &cur_psn );
01552         }
01553       }
01554     }
01555     
01556     // imm: keycode handler stub setting - use Gestalt to determine the running system version,
01557     // then set the keycode_function pointer accordingly
01558     keycode_function = keycode_wrap_old; // default to pre-10.5 mechanism
01559     SInt32 MacVersion;
01560     if (Gestalt(gestaltSystemVersion, &MacVersion) == noErr)
01561     {
01562       if(MacVersion >= 0x1050) { // 10.5.0 or later
01563         keycode_function = keycodeToUnicode;
01564       }
01565     }
01566   }
01567 }
01568 
01569 
01573 void fl_close_display()  {
01574 }
01575 
01576 
01580 int Fl::x() {
01581   BitMap r;
01582   GetQDGlobalsScreenBits(&r);
01583   return r.bounds.left;
01584 }
01585 
01586 
01590 int Fl::y() {
01591   BitMap r;
01592   GetQDGlobalsScreenBits(&r);
01593   return r.bounds.top + 20; // \todo 20 pixel menu bar?
01594 }
01595 
01596 
01600 int Fl::w() {
01601   BitMap r;
01602   GetQDGlobalsScreenBits(&r);
01603   return r.bounds.right - r.bounds.left;
01604 }
01605 
01606 
01610 int Fl::h() {
01611   BitMap r;
01612   GetQDGlobalsScreenBits(&r);
01613   return r.bounds.bottom - r.bounds.top - 20;
01614 }
01615 
01616 
01620 void Fl::get_mouse(int &x, int &y) 
01621 {
01622   fl_open_display();
01623   Point loc; 
01624   GetMouse( &loc );
01625   LocalToGlobal( &loc );
01626   x = loc.h;
01627   y = loc.v;
01628 }
01629 
01630 
01634 unsigned short mac2fltk(ulong macKey) 
01635 {
01636   unsigned short cc = macKeyLookUp[(macKey>>8)&0x7f];
01637   if (cc) return cc;
01638   return macKey&0xff;
01639 }
01640 
01641 
01645 void Fl_X::flush()
01646 {
01647   w->flush();
01648   if (fl_gc) 
01649     CGContextFlush(fl_gc);
01650   SetOrigin( 0, 0 );
01651 }
01652 
01653 
01660 void handleUpdateEvent( WindowPtr xid ) 
01661 {
01662   Fl_Window *window = fl_find( xid );
01663   if ( !window ) return;
01664   GrafPtr oldPort;
01665   GetPort( &oldPort );
01666   SetPort( GetWindowPort(xid) );
01667   Fl_X *i = Fl_X::i( window );
01668   i->wait_for_expose = 0;
01669   if ( window->damage() ) {
01670     if ( i->region ) {
01671       InvalWindowRgn( xid, i->region );
01672     }
01673   }
01674   if ( i->region ) { // no region, so the sytem will take the update region from the OS
01675     DisposeRgn( i->region );
01676     i->region = 0;
01677   }
01678   for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext )
01679   {
01680     cx->w->clear_damage(window->damage()|FL_DAMAGE_EXPOSE);
01681     cx->flush();
01682     cx->w->clear_damage();
01683   }
01684   window->clear_damage(window->damage()|FL_DAMAGE_EXPOSE);
01685   i->flush();
01686   window->clear_damage();
01687   SetPort( oldPort );
01688 }     
01689 
01690 // Gets the border sizes and the titlebar size
01691 static void get_window_frame_sizes(int &bx, int &by, int &bt) {
01692 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01693         static HIRect contentRect = { {50,50}, {100,100} }; // a rect to stand in for the content rect of a real window
01694         static HIThemeWindowDrawInfo metrics= {0, 
01695                 kThemeStateActive, kThemeDocumentWindow,
01696                 kThemeWindowHasFullZoom + kThemeWindowHasCloseBox + 
01697                 kThemeWindowHasCollapseBox + kThemeWindowHasTitleText, 
01698                 0, 0};
01699         HIShapeRef shape1=0, shape2=0, shape3=0;
01700         HIRect rect1, rect2, rect3;
01701         OSStatus        status;
01702         status  = HIThemeGetWindowShape(&contentRect, &metrics, kWindowStructureRgn, &shape1);
01703         status |= HIThemeGetWindowShape(&contentRect, &metrics, kWindowContentRgn, &shape2);
01704         status |= HIThemeGetWindowShape(&contentRect, &metrics, kWindowTitleBarRgn, &shape3);
01705     
01706         if (!status) 
01707         {
01708                 HIShapeGetBounds(shape1, &rect1);
01709                 HIShapeGetBounds(shape2, &rect2);
01710                 HIShapeGetBounds(shape3, &rect3);
01711                 bt = rect3.size.height;
01712                 bx = rect2.origin.x  - rect1.origin.x;
01713                 by = rect2.origin.y  - rect1.origin.y - bt;
01714                 // fprintf(stderr, "HIThemeGetWindowShape succeeded bx=%d by=%d bt=%d\n", bx, by, bt);
01715         }               
01716         else 
01717 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01718         {
01719                 // sets default dimensions
01720                 bx = by = 6;
01721                 bt = 22;
01722                 // fprintf(stderr, "HIThemeGetWindowShape failed, bx=%d by=%d bt=%d\n", bx, by, bt);
01723         }
01724 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01725         CFRelease(shape1); // we must free HIThemeGetWindowShape() (copied) handles 
01726         CFRelease(shape2);
01727         CFRelease(shape3);
01728 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01729 }
01730 
01734 int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) {
01735   int W, H, xoff, yoff, dx, dy;
01736   int ret = bx = by = bt = 0;
01737   if (w->border() && !w->parent()) {
01738     if (w->maxw != w->minw || w->maxh != w->minh) {
01739       ret = 2;
01740       get_window_frame_sizes(bx, by, bt);
01741       /*
01742         bx = 6; // \todo Mac : GetSystemMetrics(SM_CXSIZEFRAME);
01743         by = 6; // \todo Mac : get Mac window frame size GetSystemMetrics(SM_CYSIZEFRAME);
01744       */
01745     } else {
01746       ret = 1;
01747       get_window_frame_sizes(bx, by, bt);
01748       /*
01749         bx = 6; // \todo Mac : GetSystemMetrics(SM_CXFIXEDFRAME);
01750         by = 6; // \todo Mac : GetSystemMetrics(SM_CYFIXEDFRAME);
01751       */
01752     }
01753   }
01754   //The coordinates of the whole window, including non-client area
01755   xoff = bx;
01756   yoff = by + bt;
01757   dx = 2*bx;
01758   dy = 2*by + bt;
01759   X = w->x()-xoff;
01760   Y = w->y()-yoff;
01761   W = w->w()+dx;
01762   H = w->h()+dy;
01763 
01764   //Proceed to positioning the window fully inside the screen, if possible
01765 
01766   // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk
01767   // that we want to avoid when positioning our window, namely the Dock and the
01768   // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the
01769   // list of all available screens and find the one that this window is most
01770   // likely to go to, and then reposition it to fit withing the 'good' area.
01771   Rect r;
01772   // find the screen, that the center of this window will fall into
01773   int R = X+W, B = Y+H; // right and bottom
01774   int cx = (X+R)/2, cy = (Y+B)/2; // center of window;
01775   GDHandle gd = 0L;
01776   for (gd = GetDeviceList(); gd; gd = GetNextDevice(gd)) {
01777   GDPtr gp = *gd;
01778   if (    cx >= gp->gdRect.left && cx <= gp->gdRect.right
01779        && cy >= gp->gdRect.top  && cy <= gp->gdRect.bottom)
01780     break;
01781   }
01782   // if the center doesn't fall on a screen, try the top left
01783   if (!gd) {
01784     for (gd = GetDeviceList(); gd; gd = GetNextDevice(gd)) {
01785       GDPtr gp = *gd;
01786       if (    X >= gp->gdRect.left && X <= gp->gdRect.right
01787            && Y >= gp->gdRect.top  && Y <= gp->gdRect.bottom)
01788         break;
01789     }
01790   }
01791   // if that doesn't fall on a screen, try the top right
01792   if (!gd) {
01793     for (gd = GetDeviceList(); gd; gd = GetNextDevice(gd)) {
01794       GDPtr gp = *gd;
01795       if (    R >= gp->gdRect.left && R <= gp->gdRect.right
01796            && Y >= gp->gdRect.top  && Y <= gp->gdRect.bottom)
01797         break;
01798     }
01799   }
01800   // if that doesn't fall on a screen, try the bottom left
01801   if (!gd) {
01802     for (gd = GetDeviceList(); gd; gd = GetNextDevice(gd)) {
01803       GDPtr gp = *gd;
01804       if (    X >= gp->gdRect.left && X <= gp->gdRect.right
01805            && B >= gp->gdRect.top  && B <= gp->gdRect.bottom)
01806         break;
01807     }
01808   }
01809   // last resort, try the bottom right
01810   if (!gd) {
01811     for (gd = GetDeviceList(); gd; gd = GetNextDevice(gd)) {
01812       GDPtr gp = *gd;
01813       if (    R >= gp->gdRect.left && R <= gp->gdRect.right
01814            && B >= gp->gdRect.top  && B <= gp->gdRect.bottom)
01815         break;
01816     }
01817   }
01818   // if we still have not found a screen, we will use the main
01819   // screen, the one that has the application menu bar.
01820   if (!gd) gd = GetMainDevice();
01821   if (gd) {
01822     GetAvailableWindowPositioningBounds(gd, &r);
01823     if ( R > r.right )  X -= R - r.right;
01824     if ( B > r.bottom ) Y -= B - r.bottom;
01825     if ( X < r.left )   X = r.left;
01826     if ( Y < r.top )    Y = r.top;
01827   }
01828 
01829   //Return the client area's top left corner in (X,Y)
01830   X+=xoff;
01831   Y+=yoff;
01832 
01833   return ret;
01834 }
01835 
01839 static int FSSpec2UnixPath( FSSpec *fs, char *dst )
01840 {
01841   FSRef fsRef;
01842   FSpMakeFSRef( fs, &fsRef );
01843   FSRefMakePath( &fsRef, (UInt8*)dst, 1024 );
01844   return strlen(dst);
01845 }
01846 static void convert_crlf(char * s, size_t len)
01847 {
01848   // turn all \r characters into \n:
01849   for (size_t x = 0; x < len; x++) if (s[x] == '\r') s[x] = '\n';
01850 }
01851 
01852 
01853 static DragReference currDragRef = 0;
01854 static char *currDragData = 0L;
01855 static int currDragSize = 0; 
01856 static OSErr currDragErr = noErr;
01857 Fl_Window *fl_dnd_target_window = 0;
01858 #include <FL/fl_draw.H>
01859 
01863 static OSErr fillCurrentDragData(DragReference dragRef)
01864 {
01865   OSErr ret = noErr;
01866   char *dst = 0L;
01867   
01868   // shortcut through this whole procedure if this is still the same drag event
01869   if (dragRef==currDragRef)
01870     return currDragErr;
01871   
01872   // clear currDrag* for a new drag event
01873   currDragRef = dragRef;
01874   if (currDragData) free(currDragData);
01875   currDragData = 0;
01876   currDragSize = 0;
01877   
01878   // fill currDRag* with ASCII data, if available
01879   UInt16 i, nItem;
01880   ItemReference itemRef;
01881   FlavorFlags flags;
01882   Size itemSize, size = 0;
01883   CountDragItems( dragRef, &nItem );
01884 
01885   for ( i = 1; i <= nItem; i++ )
01886   {
01887     GetDragItemReferenceNumber( dragRef, i, &itemRef );
01888     ret = GetFlavorFlags( dragRef, itemRef, 'utf8', &flags );
01889     if ( ret == noErr )
01890     {
01891       GetFlavorDataSize( dragRef, itemRef, 'utf8', &itemSize );
01892       size += itemSize;
01893       continue;
01894     }
01895     ret = GetFlavorFlags( dragRef, itemRef, 'utxt', &flags );
01896     if ( ret == noErr )
01897     {
01898       GetFlavorDataSize( dragRef, itemRef, 'utxt', &itemSize );
01899       size += itemSize;
01900       continue;
01901     }
01902     ret = GetFlavorFlags( dragRef, itemRef, 'TEXT', &flags );
01903     if ( ret == noErr )
01904     {
01905       GetFlavorDataSize( dragRef, itemRef, 'TEXT', &itemSize );
01906       size += itemSize;
01907       continue;
01908     }
01909     ret = GetFlavorFlags( dragRef, itemRef, 'hfs ', &flags );
01910     if ( ret == noErr )
01911     {
01912       size += 1024; //++ ouch! We should create the full pathname and figure out its length
01913       continue;
01914     }
01915   }
01916 
01917   if ( !size )
01918   {
01919     currDragErr = userCanceledErr;
01920     return currDragErr;
01921   }
01922 
01923   currDragSize = size + nItem - 1;
01924   currDragData = dst = (char*)malloc( size+nItem );;
01925 
01926   for ( i = 1; i <= nItem; i++ )
01927   {
01928     GetDragItemReferenceNumber( dragRef, i, &itemRef );
01929     ret = GetFlavorFlags( dragRef, itemRef, 'utf8', &flags );
01930     if ( ret == noErr )
01931     {
01932       GetFlavorDataSize( dragRef, itemRef, 'utf8', &itemSize );
01933       GetFlavorData( dragRef, itemRef, 'utf8', dst, &itemSize, 0L );
01934       dst += itemSize;
01935       *dst++ = '\n'; // add our element separator
01936       continue;
01937     }
01938     GetDragItemReferenceNumber( dragRef, i, &itemRef );
01939     ret = GetFlavorFlags( dragRef, itemRef, 'utxt', &flags );
01940     if ( ret == noErr )
01941     {
01942       GetFlavorDataSize( dragRef, itemRef, 'utxt', &itemSize );
01943       GetFlavorData( dragRef, itemRef, 'utxt', dst, &itemSize, 0L );
01944       dst += itemSize;
01945       *dst++ = '\n'; // add our element separator
01946       continue;
01947     }
01948     ret = GetFlavorFlags( dragRef, itemRef, 'TEXT', &flags );
01949     if ( ret == noErr )
01950     {
01951       GetFlavorDataSize( dragRef, itemRef, 'TEXT', &itemSize );
01952       GetFlavorData( dragRef, itemRef, 'TEXT', dst, &itemSize, 0L );
01953       dst += itemSize;
01954       *dst++ = '\n'; // add our element separator
01955       continue;
01956     }
01957     ret = GetFlavorFlags( dragRef, itemRef, 'hfs ', &flags );
01958     if ( ret == noErr )
01959     {
01960       HFSFlavor hfs; itemSize = sizeof( hfs );
01961       GetFlavorData( dragRef, itemRef, 'hfs ', &hfs, &itemSize, 0L );
01962       itemSize = FSSpec2UnixPath( &hfs.fileSpec, dst ); // return the path name in UTF8
01963       dst += itemSize;
01964       if ( itemSize>1 && ( hfs.fileType=='fold' || hfs.fileType=='disk' ) ) 
01965         *dst++ = '/';
01966       *dst++ = '\n'; // add our element separator
01967       continue;
01968     }
01969   }
01970 
01971   dst[-1] = 0;
01972   currDragSize = dst - currDragData - 1;
01973   currDragErr = ret;
01974   return ret;
01975 }
01976 
01980 static pascal OSErr dndTrackingHandler( DragTrackingMessage msg, WindowPtr w, void *userData, DragReference dragRef )
01981 {
01982   Fl_Window *target = (Fl_Window*)userData;
01983   Fl::first_window(target);
01984   Point mp;
01985   static int px, py;
01986   
01987   fillCurrentDragData(dragRef);
01988   Fl::e_length = currDragSize;
01989   Fl::e_text = currDragData;
01990   
01991   switch ( msg )
01992   {
01993   case kDragTrackingEnterWindow:
01994     // check if 'TEXT' is available
01995     GetDragMouse( dragRef, &mp, 0 );
01996     Fl::e_x_root = px = mp.h;
01997     Fl::e_y_root = py = mp.v;
01998     Fl::e_x = px - target->x();
01999     Fl::e_y = py - target->y();
02000     fl_dnd_target_window = target;
02001     if ( Fl::handle( FL_DND_ENTER, target ) )
02002       fl_cursor( FL_CURSOR_HAND ); //ShowDragHilite( ); // modify the mouse cursor?!
02003     else
02004       fl_cursor( FL_CURSOR_DEFAULT ); //HideDragHilite( dragRef );
02005     breakMacEventLoop();
02006     return noErr;
02007   case kDragTrackingInWindow:
02008     GetDragMouse( dragRef, &mp, 0 );
02009     if ( mp.h==px && mp.v==py )
02010       break;    //+ return previous condition for dnd hiliting
02011     Fl::e_x_root = px = mp.h;
02012     Fl::e_y_root = py = mp.v;
02013     Fl::e_x = px - target->x();
02014     Fl::e_y = py - target->y();
02015     fl_dnd_target_window = target;
02016     if ( Fl::handle( FL_DND_DRAG, target ) )
02017       fl_cursor( FL_CURSOR_HAND ); //ShowDragHilite( ); // modify the mouse cursor?!
02018     else
02019       fl_cursor( FL_CURSOR_DEFAULT ); //HideDragHilite( dragRef );
02020     breakMacEventLoop();
02021     return noErr;
02022     break;
02023   case kDragTrackingLeaveWindow:
02024     // HideDragHilite()
02025     fl_cursor( FL_CURSOR_DEFAULT ); //HideDragHilite( dragRef );
02026     if ( fl_dnd_target_window )
02027     {
02028       Fl::handle( FL_DND_LEAVE, fl_dnd_target_window );
02029       fl_dnd_target_window = 0;
02030     }
02031     breakMacEventLoop();
02032     return noErr;
02033   }
02034   return noErr;
02035 }
02036 
02037 
02041 static pascal OSErr dndReceiveHandler( WindowPtr w, void *userData, DragReference dragRef )
02042 {
02043   Point mp;
02044   OSErr ret;
02045   
02046   Fl_Window *target = fl_dnd_target_window = (Fl_Window*)userData;
02047   Fl::first_window(target);
02048   GetDragMouse( dragRef, &mp, 0 );
02049   Fl::e_x_root = mp.h;
02050   Fl::e_y_root = mp.v;
02051   Fl::e_x = Fl::e_x_root - target->x();
02052   Fl::e_y = Fl::e_y_root - target->y();
02053   if ( !Fl::handle( FL_DND_RELEASE, target ) )
02054     return userCanceledErr;
02055 
02056   ret = fillCurrentDragData(dragRef);
02057   if (ret==userCanceledErr)
02058     return userCanceledErr;
02059   
02060   Fl::e_length = currDragSize;
02061   Fl::e_text = currDragData;
02062 //  printf("Sending following text to widget %p:\n%s\n", Fl::belowmouse(), Fl::e_text);
02063   int old_event = Fl::e_number;
02064   Fl::belowmouse()->handle(Fl::e_number = FL_PASTE);
02065   Fl::e_number = old_event;
02066   
02067   if (currDragData) {
02068     free(currDragData);
02069   }
02070   currDragData = 0L;
02071   currDragRef = 0;
02072   Fl::e_text = 0L;
02073   Fl::e_length = 0;
02074   fl_dnd_target_window = 0L;
02075   
02076   breakMacEventLoop();
02077   return noErr;
02078 }
02079 // fc:  
02080 static void  q_set_window_title(Window xid, const char * name ) {
02081 #if 1
02082     CFStringRef utf8_title = CFStringCreateWithCString(NULL, (name ? name : ""), kCFStringEncodingUTF8);
02083     SetWindowTitleWithCFString(xid, utf8_title);
02084     CFRelease(utf8_title);
02085 #else // old non-utf8 code to remove after new utf8 code approval :
02086     Str255 pTitle;
02087     if (name) {
02088       if (strlen(name) > 255) pTitle[0] = 255;
02089       else pTitle[0] = strlen(name);
02090       memcpy(pTitle+1, name, pTitle[0]);
02091     } 
02092     else 
02093       pTitle[0] = 0;
02094     SetWTitle(xid, pTitle);
02095 #endif
02096 }
02097 
02102 void Fl_X::make(Fl_Window* w)
02103 {
02104   static int xyPos = 100;
02105   if ( w->parent() ) // create a subwindow
02106   {
02107     Fl_Group::current(0);
02108     Rect wRect;
02109     wRect.top    = w->y();
02110     wRect.left   = w->x();
02111     wRect.bottom = w->y() + w->h(); if (wRect.bottom<=wRect.top) wRect.bottom = wRect.top+1;
02112     wRect.right  = w->x() + w->w(); if (wRect.right<=wRect.left) wRect.right = wRect.left+1;
02113     // our subwindow needs this structure to know about its clipping. 
02114     Fl_X* x = new Fl_X;
02115     x->other_xid = 0;
02116     x->region = 0;
02117     x->subRegion = 0;
02118     x->cursor = fl_default_cursor;
02119     x->gc = 0; // stay 0 for Quickdraw; fill with CGContext for Quartz
02120     Fl_Window *win = w->window();
02121     Fl_X *xo = Fl_X::i(win);
02122     if (xo) {
02123       x->xidNext = xo->xidChildren;
02124       x->xidChildren = 0L;
02125       xo->xidChildren = x;
02126       x->xid = fl_xid(win);
02127       x->w = w; w->i = x;
02128       x->wait_for_expose = 0;
02129       x->next = Fl_X::first; // must be in the list for ::flush()
02130       Fl_X::first = x;
02131       int old_event = Fl::e_number;
02132       w->handle(Fl::e_number = FL_SHOW);
02133       Fl::e_number = old_event;
02134       w->redraw(); // force draw to happen
02135     }
02136     fl_show_iconic = 0;
02137   }
02138   else // create a desktop window
02139   {
02140     Fl_Group::current(0);
02141     fl_open_display();
02142     int winclass = kDocumentWindowClass;
02143     int winattr = kWindowStandardHandlerAttribute | kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute;
02144     int xp = w->x();
02145     int yp = w->y();
02146     int wp = w->w();
02147     int hp = w->h();
02148     if (w->size_range_set) {
02149       if ( w->minh != w->maxh || w->minw != w->maxw)
02150         winattr |= kWindowFullZoomAttribute | kWindowResizableAttribute | kWindowLiveResizeAttribute;
02151     } else {
02152       if (w->resizable()) {
02153         Fl_Widget *o = w->resizable();
02154         int minw = o->w(); if (minw > 100) minw = 100;
02155         int minh = o->h(); if (minh > 100) minh = 100;
02156         w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
02157         winattr |= kWindowFullZoomAttribute | kWindowResizableAttribute | kWindowLiveResizeAttribute;
02158       } else {
02159         w->size_range(w->w(), w->h(), w->w(), w->h());
02160       }
02161     }
02162     int xwm = xp, ywm = yp, bt, bx, by;
02163 
02164     if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) {
02165       // menu windows and tooltips
02166       if (w->modal()||w->override()) {
02167         winclass = kHelpWindowClass;
02168         winattr  = 0;
02169       } else {
02170         winattr = 512; // kWindowNoTitleBarAttribute;
02171       }
02172     } else if (w->modal()) {
02173       winclass = kMovableModalWindowClass;
02174     }
02175 
02176     if (by+bt) {
02177       wp += 2*bx;
02178       hp += 2*by+bt;
02179     }
02180     if (!(w->flags() & Fl_Widget::FORCE_POSITION)) {
02181       // use the Carbon functions below for default window positioning
02182       w->x(xyPos+Fl::x());
02183       w->y(xyPos+Fl::y());
02184       xyPos += 25;
02185       if (xyPos>200) xyPos = 100;
02186     } else {
02187       if (!Fl::grab()) {
02188         xp = xwm; yp = ywm;
02189         w->x(xp);w->y(yp);
02190       }
02191       xp -= bx;
02192       yp -= by+bt;
02193     }
02194 
02195     if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) {
02196       // find some other window to be "transient for":
02197       Fl_Window* w = Fl_X::first->w;
02198       while (w->parent()) w = w->window(); // todo: this code does not make any sense! (w!=w??)
02199     }
02200 
02201     Rect wRect;
02202     wRect.top    = w->y();
02203     wRect.left   = w->x();
02204     wRect.bottom = w->y() + w->h(); if (wRect.bottom<=wRect.top) wRect.bottom = wRect.top+1;
02205     wRect.right  = w->x() + w->w(); if (wRect.right<=wRect.left) wRect.right = wRect.left+1;
02206 
02207     const char *name = w->label();
02208 
02209     Fl_X* x = new Fl_X;
02210     x->other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows
02211     x->region = 0;
02212     x->subRegion = 0;
02213     x->cursor = fl_default_cursor;
02214     x->xidChildren = 0;
02215     x->xidNext = 0;
02216     x->gc = 0;
02217 
02218     winattr &= GetAvailableWindowAttributes( winclass );        // make sure that the window will open
02219     CreateNewWindow( winclass, winattr, &wRect, &(x->xid) );
02220     q_set_window_title(x->xid, name);
02221     MoveWindow(x->xid, wRect.left, wRect.top, 1);       // avoid Carbon Bug on old OS
02222     if (w->non_modal() && !w->modal()) {
02223       // Major kludge: this is to have the regular look, but stay above the document windows
02224       SetWindowClass(x->xid, kFloatingWindowClass);
02225       SetWindowActivationScope(x->xid, kWindowActivationScopeAll);
02226     }
02227     if (!(w->flags() & Fl_Widget::FORCE_POSITION))
02228     {
02229       WindowRef pw = Fl_X::first ? Fl_X::first->xid : 0 ;
02230       if (w->modal()) {
02231         RepositionWindow(x->xid, pw, kWindowAlertPositionOnParentWindowScreen);
02232       } else if (w->non_modal()) {
02233         RepositionWindow(x->xid, pw, kWindowCenterOnParentWindowScreen);
02234       } else {
02235         RepositionWindow(x->xid, pw, kWindowCascadeOnParentWindowScreen);
02236       }
02237     }
02238     x->w = w; w->i = x;
02239     x->wait_for_expose = 1;
02240     x->next = Fl_X::first;
02241     Fl_X::first = x;
02242     { // Install Carbon Event handlers 
02243       OSStatus ret;
02244       EventHandlerUPP mousewheelHandler = NewEventHandlerUPP( carbonMousewheelHandler ); // will not be disposed by Carbon...
02245       static EventTypeSpec mousewheelEvents[] = {
02246         { kEventClassMouse, kEventMouseWheelMoved } };
02247       ret = InstallWindowEventHandler( x->xid, mousewheelHandler,
02248                 (int)(sizeof(mousewheelEvents)/sizeof(mousewheelEvents[0])),
02249                 mousewheelEvents, w, 0L );
02250       EventHandlerUPP mouseHandler = NewEventHandlerUPP( carbonMouseHandler ); // will not be disposed by Carbon...
02251       static EventTypeSpec mouseEvents[] = {
02252         { kEventClassMouse, kEventMouseDown },
02253         { kEventClassMouse, kEventMouseUp },
02254         { kEventClassMouse, kEventMouseMoved },
02255         { kEventClassMouse, kEventMouseDragged } };
02256       ret = InstallWindowEventHandler( x->xid, mouseHandler, 4, mouseEvents, w, 0L );
02257 
02258       EventHandlerUPP keyboardHandler = NewEventHandlerUPP( carbonKeyboardHandler ); // will not be disposed by Carbon...
02259       static EventTypeSpec keyboardEvents[] = {
02260         { kEventClassKeyboard, kEventRawKeyDown },
02261         { kEventClassKeyboard, kEventRawKeyRepeat },
02262         { kEventClassKeyboard, kEventRawKeyUp },
02263         { kEventClassKeyboard, kEventRawKeyModifiersChanged } };
02264       ret = InstallWindowEventHandler( x->xid, keyboardHandler, 4, keyboardEvents, w, 0L );
02265 
02266       EventHandlerUPP textHandler = NewEventHandlerUPP( carbonTextHandler ); // will not be disposed by Carbon...
02267       static EventTypeSpec textEvents[] = {
02268         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
02269       ret = InstallWindowEventHandler( x->xid, textHandler, 1, textEvents, w, 0L );
02270 
02271       EventHandlerUPP windowHandler = NewEventHandlerUPP( carbonWindowHandler ); // will not be disposed by Carbon...
02272       static EventTypeSpec windowEvents[] = {
02273         { kEventClassWindow, kEventWindowDrawContent },
02274         { kEventClassWindow, kEventWindowShown },
02275         { kEventClassWindow, kEventWindowHidden },
02276         { kEventClassWindow, kEventWindowActivated },
02277         { kEventClassWindow, kEventWindowDeactivated },
02278         { kEventClassWindow, kEventWindowClose },
02279         { kEventClassWindow, kEventWindowCollapsed },
02280         { kEventClassWindow, kEventWindowExpanded },
02281         { kEventClassWindow, kEventWindowBoundsChanging },
02282         { kEventClassWindow, kEventWindowBoundsChanged } };
02283       ret = InstallWindowEventHandler( x->xid, windowHandler, 10, windowEvents, w, 0L );
02284       ret = InstallTrackingHandler( dndTrackingHandler, x->xid, w );
02285       ret = InstallReceiveHandler( dndReceiveHandler, x->xid, w );
02286     }
02287 
02288     if ( ! Fl_X::first->next ) // if this is the first window, we need to bring the application to the front
02289     { 
02290       ProcessSerialNumber psn;
02291       OSErr err = GetCurrentProcess( &psn );
02292       if ( err==noErr ) SetFrontProcess( &psn );
02293     }
02294     
02295     if (w->size_range_set) w->size_range_();
02296     
02297     if (winclass != kHelpWindowClass) {
02298       Fl_Tooltip::enter(0);
02299     }
02300     if (w->size_range_set) w->size_range_();
02301     ShowWindow(x->xid);
02302     if (fl_show_iconic) { 
02303       fl_show_iconic = 0;
02304       CollapseWindow( x->xid, true ); // \todo Mac ; untested
02305     } else {
02306       w->set_visible();
02307     }
02308 
02309     Rect rect;
02310     GetWindowBounds(x->xid, kWindowContentRgn, &rect);
02311     w->x(rect.left); w->y(rect.top);
02312     w->w(rect.right-rect.left); w->h(rect.bottom-rect.top);
02313 
02314     int old_event = Fl::e_number;
02315     w->handle(Fl::e_number = FL_SHOW);
02316     Fl::e_number = old_event;
02317     w->redraw(); // force draw to happen
02318     
02319     if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); }
02320   }
02321 }
02322 
02323 
02327 void Fl_Window::size_range_() {
02328   size_range_set = 1;
02329   HISize minSize = { minw, minh };
02330   HISize maxSize = { maxw?maxw:32000, maxh?maxh:32000 };
02331   if (i && i->xid)
02332     SetWindowResizeLimits(i->xid, &minSize, &maxSize);
02333 }
02334 
02335 
02339 const char *fl_filename_name( const char *name ) 
02340 {
02341   const char *p, *q;
02342   if (!name) return (0);
02343   for ( p = q = name ; *p ; ) 
02344   {
02345     if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) 
02346     {
02347       q = p+2;
02348       p++;
02349     }
02350     else if (p[0] == '/')
02351       q = p + 1;
02352     p++;
02353   }
02354   return q;
02355 }
02356 
02357 
02362 void Fl_Window::label(const char *name,const char */*iname*/) {
02363   Fl_Widget::label(name);
02364 
02365   if (shown() || i) {
02366     q_set_window_title(fl_xid(this), name);
02367   }
02368 }
02369 
02370 
02374 void Fl_Window::show() {
02375   image(Fl::scheme_bg_);
02376   if (Fl::scheme_bg_) {
02377     labeltype(FL_NORMAL_LABEL);
02378     align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
02379   } else {
02380     labeltype(FL_NO_LABEL);
02381   }
02382   Fl_Tooltip::exit(this);
02383   if (!shown() || !i) {
02384     Fl_X::make(this);
02385   } else {
02386       if ( !parent() )
02387       {
02388         if ( IsWindowCollapsed( i->xid ) ) CollapseWindow( i->xid, false );
02389         if (!fl_capture) {
02390           BringToFront(i->xid);
02391           SelectWindow(i->xid);
02392         }
02393       }
02394   }
02395 }
02396 
02397 
02401 void Fl_Window::resize(int X,int Y,int W,int H) {
02402   if (W<=0) W = 1; // OS X does not like zero width windows
02403   if (H<=0) H = 1;
02404   int is_a_resize = (W != w() || H != h());
02405 //  printf("Fl_Winodw::resize(X=%d, Y=%d, W=%d, H=%d), is_a_resize=%d, resize_from_system=%p, this=%p\n",
02406 //         X, Y, W, H, is_a_resize, resize_from_system, this);
02407   if (X != x() || Y != y()) set_flag(FORCE_POSITION);
02408   else if (!is_a_resize) return;
02409   if ( (resize_from_system!=this) && (!parent()) && shown()) {
02410     if (is_a_resize) {
02411       if (resizable()) {
02412         if (W<minw) minw = W; // user request for resize takes priority
02413         if (W>maxw) maxw = W; // over a previously set size_range
02414         if (H<minh) minh = H;
02415         if (H>maxh) maxh = H;
02416         size_range(minw, minh, maxw, maxh);
02417       } else {
02418         size_range(W, H, W, H);
02419       }
02420       Rect dim; dim.left=X; dim.top=Y; dim.right=X+W; dim.bottom=Y+H;
02421       SetWindowBounds(i->xid, kWindowContentRgn, &dim);
02422       Rect all; all.top=-32000; all.bottom=32000; all.left=-32000; all.right=32000;
02423       InvalWindowRect( i->xid, &all );    
02424     } else {
02425       MoveWindow(i->xid, X, Y, 0);
02426     }
02427   }
02428   resize_from_system = 0;
02429   if (is_a_resize) {
02430     Fl_Group::resize(X,Y,W,H);
02431     if (shown()) { 
02432       redraw(); 
02433     }
02434   } else {
02435     x(X); y(Y); 
02436   }
02437 }
02438 
02439 
02443 void Fl_Window::make_current() 
02444 {
02445   OSStatus err;
02446   Fl_X::q_release_context();
02447   if ( !fl_window_region )
02448     fl_window_region = NewRgn();
02449   fl_window = i->xid;
02450   current_ = this;
02451 
02452   SetPort( GetWindowPort(i->xid) ); // \todo check for the handling of doublebuffered windows
02453 
02454   int xp = 0, yp = 0;
02455   Fl_Window *win = this;
02456   while ( win ) 
02457   {
02458     if ( !win->window() )
02459       break;
02460     xp += win->x();
02461     yp += win->y();
02462     win = (Fl_Window*)win->window();
02463   }
02464   SetOrigin( -xp, -yp );
02465   
02466   SetRectRgn( fl_window_region, 0, 0, w(), h() );
02467   
02468   // \todo for performance reasons: we don't have to create this unless the child windows moved
02469   for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext )
02470   {
02471     Fl_Window *cw = cx->w;
02472     if (!cw->visible_r()) continue;
02473     Fl_Region r = NewRgn();
02474     SetRectRgn( r, cw->x() - xp, cw->y() - yp, 
02475                    cw->x() + cw->w() - xp, cw->y() + cw->h() - yp );
02476     DiffRgn( fl_window_region, r, fl_window_region );
02477     DisposeRgn( r );
02478   }
02479  
02480   err = QDBeginCGContext(GetWindowPort(i->xid), &i->gc);
02481   if (err!=noErr) 
02482     fprintf(stderr, "Error %d in QDBeginCGContext\n", (int)err);
02483   fl_gc = i->gc;
02484   CGContextSaveGState(fl_gc);
02485   Fl_X::q_fill_context();
02486 #if defined(USE_CAIRO)
02487    if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately
02488 #endif
02489 
02490   fl_clip_region( 0 );
02491   SetPortClipRegion( GetWindowPort(i->xid), fl_window_region );
02492 
02493 #if defined(USE_CAIRO)
02494   // update the cairo_t context
02495   if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this);
02496 #endif
02497 }
02498 
02499 // helper function to manage the current CGContext fl_gc
02500 extern Fl_Color fl_color_;
02501 extern class Fl_Font_Descriptor *fl_fontsize;
02502 extern void fl_font(class Fl_Font_Descriptor*);
02503 extern void fl_quartz_restore_line_style_();
02504 
02505 // FLTK has only one global graphics state. This function copies the FLTK state into the
02506 // current Quartz context
02507 void Fl_X::q_fill_context() {
02508   if (!fl_gc) return;
02509   int hgt = 0;
02510   if (fl_window) {
02511     Rect portRect; 
02512     GetPortBounds(GetWindowPort( fl_window ), &portRect);
02513     hgt = portRect.bottom-portRect.top;
02514   } else {
02515     hgt = CGBitmapContextGetHeight(fl_gc);
02516   }
02517   CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f);
02518   CGContextScaleCTM(fl_gc, 1.0f, -1.0f);
02519   fl_font(fl_fontsize);
02520   fl_color(fl_color_);
02521   fl_quartz_restore_line_style_();
02522 }
02523 
02524 // The only way to reset clipping to its original state is to pop the current graphics
02525 // state and restore the global state.
02526 void Fl_X::q_clear_clipping() {
02527   if (!fl_gc) return;
02528   CGContextRestoreGState(fl_gc);
02529   CGContextSaveGState(fl_gc);
02530 }
02531 
02532 // Give the Quartz context back to the system
02533 void Fl_X::q_release_context(Fl_X *x) {
02534   if (x && x->gc!=fl_gc) return;
02535   if (!fl_gc) return;
02536   CGContextRestoreGState(fl_gc);
02537   if (fl_window) {
02538     OSStatus err = QDEndCGContext(GetWindowPort(fl_window), &fl_gc);
02539     if (err!=noErr)
02540       fprintf(stderr, "Error %d in QDEndCGContext\n", (int)err);
02541   }
02542   fl_gc = 0;
02543 #if defined(USE_CAIRO)
02544   if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); // capture gc changes automatically to update the cairo context adequately
02545 #endif
02546 }
02547 
02548 void Fl_X::q_begin_image(CGRect &rect, int cx, int cy, int w, int h) {
02549   CGContextSaveGState(fl_gc);
02550   CGAffineTransform mx = CGContextGetCTM(fl_gc);
02551   CGRect r2 = rect;
02552   r2.origin.x -= 0.5f;
02553   r2.origin.y -= 0.5f;
02554   CGContextClipToRect(fl_gc, r2);
02555   mx.d = -1.0; mx.tx = -mx.tx;
02556   CGContextConcatCTM(fl_gc, mx);
02557   rect.origin.x = -(mx.tx+0.5f) + rect.origin.x     - cx;
02558   rect.origin.y =  (mx.ty+0.5f) - rect.origin.y - h + cy;
02559   rect.size.width = w;
02560   rect.size.height = h;
02561 }
02562 
02563 void Fl_X::q_end_image() {
02564   CGContextRestoreGState(fl_gc);
02565 }
02566 
02568 // Copy & Paste fltk implementation.
02570 
02571 // fltk 1.3 clipboard support constant definitions:
02572 const CFStringRef       flavorNames[] = {
02573   CFSTR("public.utf16-plain-text"), 
02574   CFSTR("public.utf8-plain-text"),
02575   CFSTR("com.apple.traditional-mac-plain-text") };
02576 const CFStringEncoding encodings[] = { 
02577   kCFStringEncodingUTF16, 
02578   kCFStringEncodingUTF8, 
02579   kCFStringEncodingMacRoman};
02580 const size_t handledFlavorsCount = sizeof(encodings)/sizeof(CFStringEncoding);
02581 
02582 // clipboard variables definitions :
02583 Fl_Widget *fl_selection_requestor = 0;
02584 char *fl_selection_buffer[2];
02585 int fl_selection_length[2];
02586 static int fl_selection_buffer_length[2];
02587 
02588 #ifdef USE_PASTEBOARD
02589 static PasteboardRef myPasteboard = 0;
02590 static void allocatePasteboard() {
02591   if (!myPasteboard)
02592     PasteboardCreate(kPasteboardClipboard, &myPasteboard);
02593 }
02594 #else
02595 #endif
02596 
02597 #ifndef USE_PASTEBOARD
02598 static ScrapRef myScrap = 0;
02599 #endif
02600 
02607 void Fl::copy(const char *stuff, int len, int clipboard) {
02608   if (!stuff || len<0) return;
02609   if (len+1 > fl_selection_buffer_length[clipboard]) {
02610     delete[] fl_selection_buffer[clipboard];
02611     fl_selection_buffer[clipboard] = new char[len+100];
02612     fl_selection_buffer_length[clipboard] = len+100;
02613   }
02614   memcpy(fl_selection_buffer[clipboard], stuff, len);
02615   fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
02616   fl_selection_length[clipboard] = len;
02617   if (clipboard) {
02618 #ifdef USE_PASTEBOARD
02619     // FIXME no error checking done yet!
02620     allocatePasteboard();
02621     OSStatus err = PasteboardClear(myPasteboard);
02622     if (err!=noErr) return; // clear did not work, maybe not owner of clipboard.
02623     PasteboardSynchronize(myPasteboard);
02624     CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len);
02625     if (text==NULL) return; // there was a pb creating the object, abort.
02626     err=PasteboardPutItemFlavor(myPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), text, 0);
02627     CFRelease(text);
02628 #else
02629     OSStatus err = ClearCurrentScrap(); // whatever happens we should clear the current scrap.
02630     if(err!=noErr) {myScrap=0; return;} // don't get current scrap if a prev err occured. 
02631     err = GetCurrentScrap( &myScrap );
02632     if ( err != noErr ) {
02633       myScrap = 0;
02634       return;
02635     }
02636     // Previous version changed \n to \r before sending the text, but I would
02637     // prefer to leave the local buffer alone, so a copied buffer may be
02638     // needed. Check to see if this is necessary on OS/X.
02639     PutScrapFlavor( myScrap, kScrapFlavorTypeText, 0,
02640                     len, fl_selection_buffer[1] );
02641 #endif
02642   }
02643 }
02644 
02645 // Call this when a "paste" operation happens:
02646 void Fl::paste(Fl_Widget &receiver, int clipboard) {
02647     if (clipboard) {
02648         // see if we own the selection, if not go get it:
02649         fl_selection_length[1] = 0;
02650 #ifdef USE_PASTEBOARD
02651         OSStatus err = noErr;
02652         Boolean found = false;
02653         CFDataRef flavorData = NULL;
02654         CFStringEncoding encoding = 0;
02655 
02656         allocatePasteboard();
02657         PasteboardSynchronize(myPasteboard);
02658         ItemCount nFlavor = 0, i, j;
02659         err = PasteboardGetItemCount(myPasteboard, &nFlavor);
02660         if (err==noErr) {
02661             for (i=1; i<=nFlavor; i++) {
02662                 PasteboardItemID itemID = 0;
02663                 CFArrayRef flavorTypeArray = NULL;
02664                 found = false;
02665                 err = PasteboardGetItemIdentifier(myPasteboard, i, &itemID);
02666                 if (err!=noErr) continue;
02667                 err = PasteboardCopyItemFlavors(myPasteboard, itemID, &flavorTypeArray);
02668                 if (err!=noErr) {
02669                   if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
02670                   continue;
02671                 }
02672                 CFIndex flavorCount = CFArrayGetCount(flavorTypeArray);
02673                 for (j = 0; j < handledFlavorsCount; j++) {
02674                     for (CFIndex flavorIndex=0; flavorIndex<flavorCount; flavorIndex++) {
02675                         CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
02676                         if (UTTypeConformsTo(flavorType, flavorNames[j])) {
02677                             err = PasteboardCopyItemFlavorData( myPasteboard, itemID, flavorNames[j], &flavorData );
02678                             if(err != noErr) continue;
02679                             encoding = encodings[j];
02680                             found = true;
02681                             break;
02682                         }
02683                     }
02684                     if(found) break;
02685                 }
02686                 if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
02687                 if (found) break;
02688             }
02689             if(found) {
02690                 CFIndex len = CFDataGetLength(flavorData);
02691                 CFStringRef mycfs = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(flavorData), len, encoding, false);
02692                 CFRelease(flavorData);
02693                 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(mycfs), kCFStringEncodingUTF8) + 1;
02694                 if ( len >= fl_selection_buffer_length[1] ) {
02695                     fl_selection_buffer_length[1] = len;
02696                     delete[] fl_selection_buffer[1];
02697                     fl_selection_buffer[1] = new char[len];
02698                 }
02699                 CFStringGetCString(mycfs, fl_selection_buffer[1], len, kCFStringEncodingUTF8);
02700                 CFRelease(mycfs);
02701                 len = strlen(fl_selection_buffer[1]);
02702                 fl_selection_length[1] = len;
02703                 convert_crlf(fl_selection_buffer[1],len); // turn all \r characters into \n:
02704             }
02705         }
02706 #else
02707         ScrapRef scrap = 0;
02708         if (GetCurrentScrap(&scrap) == noErr && scrap != myScrap &&
02709             GetScrapFlavorSize(scrap, kScrapFlavorTypeText, &len) == noErr) {
02710             if ( len >= fl_selection_buffer_length[1] ) {
02711                 fl_selection_buffer_length[1] = len + 32;
02712                 delete[] fl_selection_buffer[1];
02713                 fl_selection_buffer[1] = new char[len + 32];
02714             }
02715             fl_selection_length[1] = len; len++;
02716             GetScrapFlavorData( scrap, kScrapFlavorTypeText, &len,
02717                                fl_selection_buffer[1] );
02718             fl_selection_buffer[1][fl_selection_length[1]] = 0;
02719             convert_crlf(fl_selection_buffer[1],len); 
02720         }
02721 #endif
02722     }
02723     Fl::e_text = fl_selection_buffer[clipboard];
02724     Fl::e_length = fl_selection_length[clipboard];
02725     if (!Fl::e_text) Fl::e_text = (char *)"";
02726     receiver.handle(FL_PASTE);
02727 }
02728 
02729 void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
02730 {
02731    // check, if this timer slot exists already
02732    for (int i = 0;  i < mac_timer_used;  ++i) {
02733         MacTimeout& t = mac_timers[i];
02734         // if so, simply change the fire interval
02735         if (t.callback == cb  &&  t.data == data) {
02736             SetEventLoopTimerNextFireTime(t.timer, (EventTimerInterval)time);
02737             t.pending = 1;
02738             return;
02739         }
02740     }
02741     // no existing timer to use. Create a new one:
02742     int timer_id = -1;
02743     // find an empty slot in the timer array
02744     for (int i = 0;  i < mac_timer_used;  ++i) {
02745         if ( !mac_timers[i].timer ) {
02746             timer_id = i;
02747             break;
02748         }
02749     }
02750     // if there was no empty slot, append a new timer
02751     if (timer_id == -1) {
02752         // make space if needed
02753         if (mac_timer_used == mac_timer_alloc) {
02754             realloc_timers();
02755         }
02756         timer_id = mac_timer_used++;
02757     }
02758     // now install a brand new timer
02759     MacTimeout& t = mac_timers[timer_id];
02760     EventTimerInterval fireDelay = (EventTimerInterval)time;
02761     EventLoopTimerUPP  timerUPP = NewEventLoopTimerUPP(do_timer);
02762     EventLoopTimerRef  timerRef = 0;
02763     OSStatus err = InstallEventLoopTimer(GetMainEventLoop(), fireDelay, 0, timerUPP, data, &timerRef);
02764     if (err == noErr) {
02765         t.callback = cb;
02766         t.data     = data;
02767         t.timer    = timerRef;
02768         t.upp      = timerUPP;
02769         t.pending  = 1;
02770     } else {
02771         if (timerRef) 
02772             RemoveEventLoopTimer(timerRef);
02773         if (timerUPP)
02774             DisposeEventLoopTimerUPP(timerUPP);
02775     }
02776 }
02777 
02778 void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
02779 {
02780     // currently, repeat_timeout does not subtract the trigger time of the previous timer event as it should.
02781     add_timeout(time, cb, data);
02782 }
02783 
02784 int Fl::has_timeout(Fl_Timeout_Handler cb, void* data)
02785 {
02786    for (int i = 0;  i < mac_timer_used;  ++i) {
02787         MacTimeout& t = mac_timers[i];
02788         if (t.callback == cb  &&  t.data == data && t.pending) {
02789             return 1;
02790         }
02791     }
02792     return 0;
02793 }
02794 
02795 void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
02796 {
02797    for (int i = 0;  i < mac_timer_used;  ++i) {
02798         MacTimeout& t = mac_timers[i];
02799         if (t.callback == cb  && ( t.data == data || data == NULL)) {
02800             delete_timer(t);
02801         }
02802     }
02803 }
02804 
02805 int MacUnlinkWindow(Fl_X *ip, Fl_X *start) {
02806   if (!ip) return 0;
02807   if (start) {
02808     Fl_X *pc = start;
02809     while (pc) {
02810       if (pc->xidNext == ip) {
02811         pc->xidNext = ip->xidNext;
02812         return 1;
02813       }
02814       if (pc->xidChildren) {
02815         if (pc->xidChildren == ip) {
02816           pc->xidChildren = ip->xidNext;
02817           return 1;
02818         }
02819         if (MacUnlinkWindow(ip, pc->xidChildren))
02820           return 1;
02821       }
02822       pc = pc->xidNext;
02823     }
02824   } else {
02825     for ( Fl_X *pc = Fl_X::first; pc; pc = pc->next ) {
02826       if (MacUnlinkWindow(ip, pc))
02827         return 1;
02828     }
02829   }  
02830   return 0;
02831 }
02832 
02833 static void MacRelinkWindow(Fl_X *x, Fl_X *p) {
02834   if (!x || !p) return;
02835   // first, check if 'x' is already registered as a child of 'p'
02836   for (Fl_X *i = p->xidChildren; i; i=i->xidNext) {
02837     if (i == x) return;
02838   }
02839   // now add 'x' as the first child of 'p'
02840   x->xidNext = p->xidChildren;
02841   p->xidChildren = x;
02842 }
02843 
02844 void MacDestroyWindow(Fl_Window *w, WindowPtr p) {
02845   MacUnmapWindow(w, p);
02846   if (w && !w->parent() && p)
02847     DisposeWindow(p);
02848 }
02849 
02850 void MacMapWindow(Fl_Window *w, WindowPtr p) {
02851   if (w && p)
02852     ShowWindow(p);
02853   //+ link to window list
02854   if (w && w->parent()) {
02855     MacRelinkWindow(Fl_X::i(w), Fl_X::i(w->window()));
02856     w->redraw();
02857   }
02858 }
02859 
02860 void MacUnmapWindow(Fl_Window *w, WindowPtr p) {
02861   if (w && !w->parent() && p) 
02862     HideWindow(p);
02863   if (w && Fl_X::i(w)) 
02864     MacUnlinkWindow(Fl_X::i(w));
02865 }
02866 #endif // FL_DOXYGEN
02867 
02868 //
02869 // End of "$Id: Fl_mac.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $".
02870 //