|
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) ![]() |
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, ¤tBounds ); 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, ¤tBounds ); 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(¤tLayout); 01135 if (currentLayout) { 01136 KLGetKeyboardLayoutProperty(currentLayout, kKLIdentifier, (const void**)¤tLayoutId); 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 //