2 // "$Id: Fl_mac.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $"
4 // MacOS specific code for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 //// From the inner edge of a MetroWerks CodeWarrior CD:
29 // (without permission)
31 // "Three Compiles for 68Ks under the sky,
32 // Seven Compiles for PPCs in their fragments of code,
33 // Nine Compiles for Mortal Carbon doomed to die,
34 // One Compile for Mach-O Cocoa on its Mach-O throne,
35 // in the Land of MacOS X where the Drop-Shadows lie.
37 // One Compile to link them all, One Compile to merge them,
38 // One Compile to copy them all and in the bundle bind them,
39 // in the Land of MacOS X where the Drop-Shadows lie."
41 // warning: the Apple Quartz version still uses some Quickdraw calls,
42 // mostly to get around the single active context in QD and
43 // to implement clipping. This should be changed into pure
44 // Quartz calls in the near future.
46 // FIXME moving away from Carbon, I am replacing the Scrap manager calls with Pasteboard
47 // calls that support utf8 encoding. As soon as these function haven proven working
48 // the Scrap manager calls should be removed
49 #define USE_PASTEBOARD 1
51 // we don't need the following definition because we deliver only
52 // true mouse moves. On very slow systems however, this flag may
56 #define CONSOLIDATE_MOTION 0
64 #include <FL/Fl_Window.H>
65 #include <FL/Fl_Tooltip.H>
66 #include <FL/Fl_Sys_Menu_Bar.H>
72 // #define DEBUG_SELECT // UNCOMMENT FOR SELECT()/THREAD DEBUGGING
74 #include <stdio.h> // testing
75 #define DEBUGMSG(msg) if ( msg ) fprintf(stderr, msg);
76 #define DEBUGPERRORMSG(msg) if ( msg ) perror(msg)
77 #define DEBUGTEXT(txt) txt
80 #define DEBUGPERRORMSG(msg)
81 #define DEBUGTEXT(txt) NULL
82 #endif /*DEBUG_SELECT*/
85 extern Fl_Window
* fl_find(Window
);
86 extern void fl_fix_focus();
88 // forward definition of functions in this file
89 static void handleUpdateEvent( WindowPtr xid
);
90 //+ int fl_handle(const EventRecord &event);
91 static int FSSpec2UnixPath( FSSpec
*fs
, char *dst
);
92 // converting cr lf converter function
93 static void convert_crlf(char * string
, size_t len
);
97 CGContextRef fl_gc
= 0;
98 Handle fl_system_menu
;
99 Fl_Sys_Menu_Bar
*fl_sys_menu_bar
= 0;
100 CursHandle fl_default_cursor
;
101 WindowRef fl_capture
= 0; // we need this to compensate for a missing(?) mouse capture
102 ulong fl_event_time
; // the last timestamp from an x event
103 char fl_key_vector
[32]; // used by Fl::get_key()
104 bool fl_show_iconic
; // true if called from iconize() - shows the next created window in collapsed state
105 int fl_disable_transient_for
; // secret method of removing TRANSIENT_FOR
106 const Fl_Window
* fl_modal_for
; // parent of modal() window
107 Fl_Region fl_window_region
= 0;
109 Fl_Window
*Fl_Window::current_
;
110 EventRef fl_os_event
; // last (mouse) event
112 // forward declarations of variables in this file
113 static int got_events
= 0;
114 static Fl_Window
* resize_from_system
;
115 static CursPtr default_cursor_ptr
;
116 static Cursor default_cursor
;
117 static WindowRef fl_os_capture
= 0; // the dispatch handler will redirect mose move and drag events to these windows
119 #if CONSOLIDATE_MOTION
120 static Fl_Window
* send_motion
;
121 extern Fl_Window
* fl_xmousewin
;
124 enum { kEventClassFLTK
= 'fltk' };
125 enum { kEventFLTKBreakLoop
= 1, kEventFLTKDataReady
};
127 /* fltk-utf8 placekeepers */
132 void fl_set_spot(int font
, int size
, int X
, int Y
, int W
, int H
, Fl_Window
*win
)
136 void fl_set_status(int x
, int y
, int w
, int h
)
141 * Mac keyboard lookup table
143 static unsigned short macKeyLookUp
[128] =
145 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x',
146 'c', 'v', '^', 'b', 'q', 'w', 'e', 'r',
148 'y', 't', '1', '2', '3', '4', '6', '5',
149 '=', '9', '7', '-', '8', '0', ']', 'o',
151 'u', '[', 'i', 'p', FL_Enter
, 'l', 'j', '\'',
152 'k', ';', '\\', ',', '/', 'n', 'm', '.',
154 FL_Tab
, ' ', '`', FL_BackSpace
,
155 FL_KP_Enter
, FL_Escape
, 0, 0/*FL_Meta_L*/,
156 0/*FL_Shift_L*/, 0/*FL_Caps_Lock*/, 0/*FL_Alt_L*/, 0/*FL_Control_L*/,
157 0/*FL_Shift_R*/, 0/*FL_Alt_R*/, 0/*FL_Control_R*/, 0,
159 0, FL_KP
+'.', FL_Right
, FL_KP
+'*', 0, FL_KP
+'+', FL_Left
, FL_Delete
,
160 FL_Down
, 0, 0, FL_KP
+'/', FL_KP_Enter
, FL_Up
, FL_KP
+'-', 0,
162 0, FL_KP
+'=', FL_KP
+'0', FL_KP
+'1', FL_KP
+'2', FL_KP
+'3', FL_KP
+'4', FL_KP
+'5',
163 FL_KP
+'6', FL_KP
+'7', 0, FL_KP
+'8', FL_KP
+'9', 0, 0, 0,
165 FL_F
+5, FL_F
+6, FL_F
+7, FL_F
+3, FL_F
+8, FL_F
+9, 0, FL_F
+11,
166 0, 0/*FL_F+13*/, FL_Print
, FL_Scroll_Lock
, 0, FL_F
+10, FL_Menu
, FL_F
+12,
168 0, FL_Pause
, FL_Help
, FL_Home
, FL_Page_Up
, FL_Delete
, FL_F
+4, FL_End
,
169 FL_F
+2, FL_Page_Down
, FL_F
+1, FL_Left
, FL_Right
, FL_Down
, FL_Up
, 0/*FL_Power*/,
173 * convert the current mouse chord into the FLTK modifier state
175 static unsigned int mods_to_e_state( UInt32 mods
)
178 if ( mods
& kEventKeyModifierNumLockMask
) state
|= FL_NUM_LOCK
;
179 if ( mods
& cmdKey
) state
|= FL_META
;
180 if ( mods
& (optionKey
|rightOptionKey
) ) state
|= FL_ALT
;
181 if ( mods
& (controlKey
|rightControlKey
) ) state
|= FL_CTRL
;
182 if ( mods
& (shiftKey
|rightShiftKey
) ) state
|= FL_SHIFT
;
183 if ( mods
& alphaLock
) state
|= FL_CAPS_LOCK
;
184 unsigned int ret
= ( Fl::e_state
& 0xff000000 ) | state
;
186 //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods );
192 * convert the current mouse chord into the FLTK keysym
194 static void mods_to_e_keysym( UInt32 mods
)
196 if ( mods
& cmdKey
) Fl::e_keysym
= FL_Meta_L
;
197 else if ( mods
& kEventKeyModifierNumLockMask
) Fl::e_keysym
= FL_Num_Lock
;
198 else if ( mods
& optionKey
) Fl::e_keysym
= FL_Alt_L
;
199 else if ( mods
& rightOptionKey
) Fl::e_keysym
= FL_Alt_R
;
200 else if ( mods
& controlKey
) Fl::e_keysym
= FL_Control_L
;
201 else if ( mods
& rightControlKey
) Fl::e_keysym
= FL_Control_R
;
202 else if ( mods
& shiftKey
) Fl::e_keysym
= FL_Shift_L
;
203 else if ( mods
& rightShiftKey
) Fl::e_keysym
= FL_Shift_R
;
204 else if ( mods
& alphaLock
) Fl::e_keysym
= FL_Caps_Lock
;
205 else Fl::e_keysym
= 0;
206 //printf( "to sym 0x%08x (%04x)\n", Fl::e_keysym, mods );
208 // these pointers are set by the Fl::lock() function:
209 static void nothing() {}
210 void (*fl_lock_function
)() = nothing
;
211 void (*fl_unlock_function
)() = nothing
;
214 // Select interface -- how it's implemented:
215 // When the user app configures one or more file descriptors to monitor
216 // with Fl::add_fd(), we start a separate thread to select() the data,
217 // sending a custom OSX 'FLTK data ready event' to the parent thread's
218 // RunApplicationLoop(), so that it triggers the data ready callbacks
219 // in the parent thread. -erco 04/04/04
225 // Class to handle select() 'data ready'
232 void (*cb
)(int, void*);
235 int nfds
, fd_array_size
;
237 pthread_t tid
; // select()'s thread id
239 // Data that needs to be locked (all start with '_')
240 pthread_mutex_t _datalock
; // data lock
241 fd_set _fdsets
[3]; // r/w/x sets user wants to monitor
242 int _maxfd
; // max fd count to monitor
243 int _cancelpipe
[2]; // pipe used to help cancel thread
244 void *_userdata
; // thread's userdata
254 pthread_mutex_init(&_datalock
, NULL
);
255 FD_ZERO(&_fdsets
[0]); FD_ZERO(&_fdsets
[1]); FD_ZERO(&_fdsets
[2]);
256 _cancelpipe
[0] = _cancelpipe
[1] = 0;
263 CancelThread(DEBUGTEXT("DESTRUCTOR\n"));
264 if (fds
) { free(fds
); fds
= 0; }
269 // The convention for locks: volatile vars start with '_',
270 // and must be locked before use. Locked code is prefixed
271 // with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco
273 void DataLock() { pthread_mutex_lock(&_datalock
); }
274 void DataUnlock() { pthread_mutex_unlock(&_datalock
); }
277 int IsThreadRunning() { return(tid
? 1 : 0); }
278 int GetNfds() { return(nfds
); }
279 int GetCancelPipe(int ix
) { return(_cancelpipe
[ix
]); }
280 fd_set
GetFdset(int ix
) { return(_fdsets
[ix
]); }
283 void AddFD(int n
, int events
, void (*cb
)(int, void*), void *v
);
284 void RemoveFD(int n
, int events
);
285 int CheckData(fd_set
& r
, fd_set
& w
, fd_set
& x
);
286 void HandleData(fd_set
& r
, fd_set
& w
, fd_set
& x
);
287 static void* DataReadyThread(void *self
);
288 void StartThread(void *userdata
);
289 void CancelThread(const char *reason
);
292 static DataReady dataready
;
294 void DataReady::AddFD(int n
, int events
, void (*cb
)(int, void*), void *v
)
298 if (i
>= fd_array_size
)
301 fd_array_size
= 2*fd_array_size
+1;
302 if (!fds
) { temp
= (FD
*)malloc(fd_array_size
*sizeof(FD
)); }
303 else { temp
= (FD
*)realloc(fds
, fd_array_size
*sizeof(FD
)); }
310 fds
[i
].events
= events
;
312 /*LOCK*/ if (events
& POLLIN
) FD_SET(n
, &_fdsets
[0]);
313 /*LOCK*/ if (events
& POLLOUT
) FD_SET(n
, &_fdsets
[1]);
314 /*LOCK*/ if (events
& POLLERR
) FD_SET(n
, &_fdsets
[2]);
315 /*LOCK*/ if (n
> _maxfd
) _maxfd
= n
;
319 // Remove an FD from the array
320 void DataReady::RemoveFD(int n
, int events
)
323 for (i
=j
=0; i
<nfds
; i
++)
327 int e
= fds
[i
].events
& ~events
;
328 if (!e
) continue; // if no events left, delete this fd
331 // move it down in the array if necessary:
338 /*LOCK*/ if (events
& POLLIN
) FD_CLR(n
, &_fdsets
[0]);
339 /*LOCK*/ if (events
& POLLOUT
) FD_CLR(n
, &_fdsets
[1]);
340 /*LOCK*/ if (events
& POLLERR
) FD_CLR(n
, &_fdsets
[2]);
341 /*LOCK*/ if (n
== _maxfd
) _maxfd
--;
345 // CHECK IF USER DATA READY, RETURNS r/w/x INDICATING WHICH IF ANY
346 int DataReady::CheckData(fd_set
& r
, fd_set
& w
, fd_set
& x
)
350 /*LOCK*/ timeval t
= { 0, 1 }; // quick check
351 /*LOCK*/ r
= _fdsets
[0], w
= _fdsets
[1], x
= _fdsets
[2];
352 /*LOCK*/ ret
= ::select(_maxfd
+1, &r
, &w
, &x
, &t
);
355 { DEBUGPERRORMSG("CheckData(): select()"); }
359 // HANDLE DATA READY CALLBACKS
360 void DataReady::HandleData(fd_set
& r
, fd_set
& w
, fd_set
& x
)
362 for (int i
=0; i
<nfds
; i
++)
366 if (FD_ISSET(f
, &r
)) revents
|= POLLIN
;
367 if (FD_ISSET(f
, &w
)) revents
|= POLLOUT
;
368 if (FD_ISSET(f
, &x
)) revents
|= POLLERR
;
369 if (fds
[i
].events
& revents
)
371 DEBUGMSG("DOING CALLBACK: ");
372 fds
[i
].cb(f
, fds
[i
].arg
);
379 // This thread watches for changes in user's file descriptors.
380 // Sends a 'data ready event' to the main thread if any change.
382 void* DataReady::DataReadyThread(void *o
)
384 DataReady
*self
= (DataReady
*)o
;
385 while ( 1 ) // loop until thread cancel or error
387 // Thread safe local copies of data before each select()
389 /*LOCK*/ int maxfd
= self
->_maxfd
;
390 /*LOCK*/ fd_set r
= self
->GetFdset(0);
391 /*LOCK*/ fd_set w
= self
->GetFdset(1);
392 /*LOCK*/ fd_set x
= self
->GetFdset(2);
393 /*LOCK*/ void *userdata
= self
->_userdata
;
394 /*LOCK*/ int cancelpipe
= self
->GetCancelPipe(0);
395 /*LOCK*/ if ( cancelpipe
> maxfd
) maxfd
= cancelpipe
;
396 /*LOCK*/ FD_SET(cancelpipe
, &r
); // add cancelpipe to fd's to watch
397 /*LOCK*/ FD_SET(cancelpipe
, &x
);
399 // timeval t = { 1000, 0 }; // 1000 seconds;
400 timeval t
= { 2, 0 }; // HACK: 2 secs prevents 'hanging' problem
401 int ret
= ::select(maxfd
+1, &r
, &w
, &x
, &t
);
402 pthread_testcancel(); // OSX 10.0.4 and older: needed for parent to cancel
409 DEBUGPERRORMSG("CHILD THREAD: select() failed");
410 return(NULL
); // error? exit thread
412 default: // DATA READY
414 if (FD_ISSET(cancelpipe
, &r
) || FD_ISSET(cancelpipe
, &x
)) // cancel?
415 { return(NULL
); } // just exit
416 DEBUGMSG("CHILD THREAD: DATA IS READY\n");
418 CreateEvent( 0, kEventClassFLTK
, kEventFLTKDataReady
,
419 0, kEventAttributeUserEvent
, &drEvent
);
420 EventQueueRef eventqueue
= (EventQueueRef
)userdata
;
421 PostEventToQueue(eventqueue
, drEvent
, kEventPriorityStandard
);
422 ReleaseEvent( drEvent
);
423 return(NULL
); // done with thread
429 // START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE
430 void DataReady::StartThread(void *new_userdata
)
432 CancelThread(DEBUGTEXT("STARTING NEW THREAD\n"));
434 /*LOCK*/ pipe(_cancelpipe
); // pipe for sending cancel msg to thread
435 /*LOCK*/ _userdata
= new_userdata
;
437 DEBUGMSG("*** START THREAD\n");
438 pthread_create(&tid
, NULL
, DataReadyThread
, (void*)this);
441 // CANCEL 'DATA READY' THREAD, CLOSE PIPE
442 void DataReady::CancelThread(const char *reason
)
446 DEBUGMSG("*** CANCEL THREAD: ");
448 if ( pthread_cancel(tid
) == 0 ) // cancel first
451 /*LOCK*/ write(_cancelpipe
[1], "x", 1); // wake thread from select
453 pthread_join(tid
, NULL
); // wait for thread to finish
456 DEBUGMSG("(JOINED) OK\n");
458 // Close pipe if open
460 /*LOCK*/ if ( _cancelpipe
[0] ) { close(_cancelpipe
[0]); _cancelpipe
[0] = 0; }
461 /*LOCK*/ if ( _cancelpipe
[1] ) { close(_cancelpipe
[1]); _cancelpipe
[1] = 0; }
465 void Fl::add_fd( int n
, int events
, void (*cb
)(int, void*), void *v
)
466 { dataready
.AddFD(n
, events
, cb
, v
); }
468 void Fl::add_fd(int fd
, void (*cb
)(int, void*), void* v
)
469 { dataready
.AddFD(fd
, POLLIN
, cb
, v
); }
471 void Fl::remove_fd(int n
, int events
)
472 { dataready
.RemoveFD(n
, events
); }
474 void Fl::remove_fd(int n
)
475 { dataready
.RemoveFD(n
, -1); }
478 * Check if there is actually a message pending!
483 return !ReceiveNextEvent(0, NULL
, 0.0, false, &event
);
487 * handle Apple Menu items (can be created using the Fl_Sys_Menu_Bar
488 * returns eventNotHandledErr if the menu item could not be handled
490 OSStatus
HandleMenu( HICommand
*cmd
)
492 OSStatus ret
= eventNotHandledErr
;
493 // attributes, commandIDm menu.menuRef, menu.menuItemIndex
495 OSErr rrc
= GetMenuItemRefCon( cmd
->menu
.menuRef
, cmd
->menu
.menuItemIndex
, &ref
);
496 //printf( "%d, %08x, %08x, %d, %d, %8x\n", rrc, cmd->attributes, cmd->commandID, cmd->menu.menuRef, cmd->menu.menuItemIndex, rrc );
497 if ( rrc
==noErr
&& ref
)
499 Fl_Menu_Item
*m
= (Fl_Menu_Item
*)ref
;
500 //printf( "Menu: %s\n", m->label() );
501 fl_sys_menu_bar
->picked( m
);
502 if ( m
->flags
& FL_MENU_TOGGLE
) // update the menu toggle symbol
503 SetItemMark( cmd
->menu
.menuRef
, cmd
->menu
.menuItemIndex
, (m
->flags
& FL_MENU_VALUE
) ? 0x12 : 0 );
504 if ( m
->flags
& FL_MENU_RADIO
) // update all radio buttons in this menu
507 int i
= cmd
->menu
.menuItemIndex
;
510 if ( j
->flags
& FL_MENU_DIVIDER
)
513 if ( !j
->text
|| !j
->radio() )
515 SetItemMark( cmd
->menu
.menuRef
, i
, ( j
->flags
& FL_MENU_VALUE
) ? 0x13 : 0 );
517 j
= m
-1; i
= cmd
->menu
.menuItemIndex
-1;
518 for ( ; i
>0; j
--, i
-- )
520 if ( !j
->text
|| j
->flags
&FL_MENU_DIVIDER
|| !j
->radio() )
522 SetItemMark( cmd
->menu
.menuRef
, i
, ( j
->flags
& FL_MENU_VALUE
) ? 0x13 : 0 );
524 SetItemMark( cmd
->menu
.menuRef
, cmd
->menu
.menuItemIndex
, ( m
->flags
& FL_MENU_VALUE
) ? 0x13 : 0 );
526 ret
= noErr
; // done handling this event
534 * We can make every event pass through this function
535 * - mouse events need to be manipulated to use a mouse focus window
536 * - keyboard, mouse and some window events need to quit the Apple Event Loop
537 * so FLTK can continue its own management
539 static pascal OSStatus
carbonDispatchHandler( EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
541 OSStatus ret
= eventNotHandledErr
;
548 switch ( GetEventClass( event
) )
550 case kEventClassMouse
:
551 switch ( GetEventKind( event
) )
554 case kEventMouseMoved
:
555 case kEventMouseDragged
:
557 ret
= SendEventToEventTarget( event
, GetWindowEventTarget( fl_capture
) );
558 else if ( fl_os_capture
){
559 ret
= SendEventToEventTarget( event
, GetWindowEventTarget( fl_os_capture
) );
565 case kEventClassCommand
:
566 switch (GetEventKind( event
) )
568 case kEventCommandProcess
:
569 ret
= GetEventParameter( event
, kEventParamDirectObject
, typeHICommand
, NULL
, sizeof(HICommand
), NULL
, &cmd
);
570 if (ret
== noErr
&& (cmd
.attributes
& kHICommandFromMenu
) != 0)
571 ret
= HandleMenu( &cmd
);
573 ret
= eventNotHandledErr
;
577 case kEventClassFLTK
:
578 switch ( GetEventKind( event
) )
580 case kEventFLTKBreakLoop
:
583 case kEventFLTKDataReady
:
585 dataready
.CancelThread(DEBUGTEXT("DATA READY EVENT\n"));
587 // CHILD THREAD TELLS US DATA READY
588 // Check to see what's ready, and invoke user's cb's
591 switch(dataready
.CheckData(r
,w
,x
))
597 default: // DATA READY
598 dataready
.HandleData(r
,w
,x
);
606 if ( ret
== eventNotHandledErr
)
607 ret
= CallNextEventHandler( nextHandler
, event
); // let the OS handle the activation, but continue to get a click-through effect
609 fl_unlock_function();
616 * break the current event loop
618 static void breakMacEventLoop()
624 CreateEvent( 0, kEventClassFLTK
, kEventFLTKBreakLoop
, 0, kEventAttributeUserEvent
, &breakEvent
);
625 PostEventToQueue( GetCurrentEventQueue(), breakEvent
, kEventPriorityStandard
);
626 ReleaseEvent( breakEvent
);
628 fl_unlock_function();
636 Fl_Timeout_Handler callback
;
638 EventLoopTimerRef timer
;
639 EventLoopTimerUPP upp
;
642 static MacTimeout
* mac_timers
;
643 static int mac_timer_alloc
;
644 static int mac_timer_used
;
647 static void realloc_timers()
649 if (mac_timer_alloc
== 0) {
652 mac_timer_alloc
*= 2;
653 MacTimeout
* new_timers
= new MacTimeout
[mac_timer_alloc
];
654 memset(new_timers
, 0, sizeof(MacTimeout
)*mac_timer_alloc
);
655 memcpy(new_timers
, mac_timers
, sizeof(MacTimeout
) * mac_timer_used
);
656 MacTimeout
* delete_me
= mac_timers
;
657 mac_timers
= new_timers
;
661 static void delete_timer(MacTimeout
& t
)
664 RemoveEventLoopTimer(t
.timer
);
665 DisposeEventLoopTimerUPP(t
.upp
);
666 memset(&t
, 0, sizeof(MacTimeout
));
671 static pascal void do_timer(EventLoopTimerRef timer
, void* data
)
673 for (int i
= 0; i
< mac_timer_used
; ++i
) {
674 MacTimeout
& t
= mac_timers
[i
];
675 if (t
.timer
== timer
&& t
.data
== data
) {
687 * This function is the central event handler.
688 * It reads events from the event queue using the given maximum time
689 * Funny enough, it returns the same time that it got as the argument.
691 static double do_queued_events( double time
= 0.0 )
693 static bool been_here
= false;
694 static RgnHandle rgn
;
696 // initialize events and a region that enables mouse move events
701 SetRectRgn(rgn
, mp
.h
, mp
.v
, mp
.h
, mp
.v
);
702 SetEventMask(everyEvent
);
706 static EventTargetRef target
= 0;
709 target
= GetEventDispatcherTarget();
711 EventHandlerUPP dispatchHandler
= NewEventHandlerUPP( carbonDispatchHandler
); // will not be disposed by Carbon...
712 static EventTypeSpec dispatchEvents
[] = {
713 { kEventClassWindow
, kEventWindowShown
},
714 { kEventClassWindow
, kEventWindowHidden
},
715 { kEventClassWindow
, kEventWindowActivated
},
716 { kEventClassWindow
, kEventWindowDeactivated
},
717 { kEventClassWindow
, kEventWindowClose
},
718 { kEventClassKeyboard
, kEventRawKeyDown
},
719 { kEventClassKeyboard
, kEventRawKeyRepeat
},
720 { kEventClassKeyboard
, kEventRawKeyUp
},
721 { kEventClassKeyboard
, kEventRawKeyModifiersChanged
},
722 { kEventClassMouse
, kEventMouseDown
},
723 { kEventClassMouse
, kEventMouseUp
},
724 { kEventClassMouse
, kEventMouseMoved
},
725 { kEventClassMouse
, 11 }, // MightyMouse wheels
726 { kEventClassMouse
, kEventMouseWheelMoved
},
727 { kEventClassMouse
, kEventMouseDragged
},
728 { kEventClassFLTK
, kEventFLTKBreakLoop
},
729 { kEventClassFLTK
, kEventFLTKDataReady
} };
730 ret
= InstallEventHandler( target
, dispatchHandler
, GetEventTypeCount(dispatchEvents
), dispatchEvents
, 0, 0L );
731 static EventTypeSpec appEvents
[] = {
732 { kEventClassCommand
, kEventCommandProcess
} };
733 ret
= InstallApplicationEventHandler( dispatchHandler
, GetEventTypeCount(appEvents
), appEvents
, 0, 0L );
738 // Check for re-entrant condition
739 if ( dataready
.IsThreadRunning() )
740 { dataready
.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); }
742 // Start thread to watch for data ready
743 if ( dataready
.GetNfds() )
744 { dataready
.StartThread((void*)GetCurrentEventQueue()); }
746 fl_unlock_function();
749 EventTimeout timeout
= time
;
750 if (!ReceiveNextEvent(0, NULL
, timeout
, true, &event
)) {
752 OSErr ret
= SendEventToEventTarget( event
, target
);
755 ConvertEventRefToEventRecord(event
, &clevent
);
756 if (clevent
.what
==kHighLevelEvent
) {
757 ret
= AEProcessAppleEvent(&clevent
);
760 if ( ret
==eventNotHandledErr
761 && GetEventClass(event
)==kEventClassMouse
762 && GetEventKind(event
)==kEventMouseDown
) {
763 WindowRef win
; Point pos
;
764 GetEventParameter(event
, kEventParamMouseLocation
, typeQDPoint
,
765 NULL
, sizeof(pos
), NULL
, &pos
);
766 if (MacFindWindow(pos
, &win
)==inMenuBar
) {
770 ReleaseEvent( event
);
775 #if CONSOLIDATE_MOTION
776 if (send_motion
&& send_motion
== fl_xmousewin
) {
778 Fl::handle(FL_MOVE
, fl_xmousewin
);
787 * This public function handles all events. It wait a maximum of
788 * 'time' secods for an event. This version returns 1 if events
789 * other than the timeout timer were processed.
791 * \todo there is no socket handling in this code whatsoever
793 int fl_wait( double time
)
795 do_queued_events( time
);
801 * event handler for Apple-Q key combination
802 * this is also called from the Carbon Window handler after all windows were closed
804 static OSErr
QuitAppleEventHandler( const AppleEvent
*appleEvt
, AppleEvent
* reply
, UInt32 refcon
)
808 while ( Fl_X::first
) {
809 Fl_X
*x
= Fl_X::first
;
810 Fl::handle( FL_CLOSE
, x
->w
);
811 if ( Fl_X::first
== x
) {
812 fl_unlock_function();
813 return noErr
; // FLTK has not close all windows, so we return to the main program now
817 fl_unlock_function();
824 * Carbon Window handler
825 * This needs to be linked into all new window event handlers
827 static pascal OSStatus
carbonWindowHandler( EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
829 UInt32 kind
= GetEventKind( event
);
830 OSStatus ret
= eventNotHandledErr
;
831 Fl_Window
*window
= (Fl_Window
*)userData
;
832 Fl::first_window(window
);
834 Rect currentBounds
, originalBounds
;
835 WindowClass winClass
;
836 static Fl_Window
*activeWindow
= 0;
842 case kEventWindowBoundsChanging
:
843 GetEventParameter( event
, kEventParamCurrentBounds
, typeQDRectangle
, NULL
, sizeof(Rect
), NULL
, ¤tBounds
);
844 GetEventParameter( event
, kEventParamOriginalBounds
, typeQDRectangle
, NULL
, sizeof(Rect
), NULL
, &originalBounds
);
846 case kEventWindowDrawContent
:
847 handleUpdateEvent( fl_xid( window
) );
850 case kEventWindowBoundsChanged
: {
851 GetEventParameter( event
, kEventParamCurrentBounds
, typeQDRectangle
, NULL
, sizeof(Rect
), NULL
, ¤tBounds
);
852 GetEventParameter( event
, kEventParamOriginalBounds
, typeQDRectangle
, NULL
, sizeof(Rect
), NULL
, &originalBounds
);
853 int X
= currentBounds
.left
, W
= currentBounds
.right
-X
;
854 int Y
= currentBounds
.top
, H
= currentBounds
.bottom
-Y
;
855 resize_from_system
= window
;
856 window
->resize( X
, Y
, W
, H
);
857 if ( ( originalBounds
.right
- originalBounds
.left
!= W
)
858 || ( originalBounds
.bottom
- originalBounds
.top
!= H
) )
860 if ( window
->shown() )
861 handleUpdateEvent( fl_xid( window
) );
864 case kEventWindowShown
:
865 if ( !window
->parent() )
867 GetWindowClass( fl_xid( window
), &winClass
);
868 if ( winClass
!= kHelpWindowClass
) { // help windows can't get the focus!
869 Fl::handle( FL_FOCUS
, window
);
870 activeWindow
= window
;
872 Fl::handle( FL_SHOW
, window
);
873 mods_to_e_state(GetCurrentKeyModifiers());
876 case kEventWindowHidden
:
877 if ( !window
->parent() ) Fl::handle( FL_HIDE
, window
);
879 case kEventWindowActivated
:
880 if ( window
->shown() && window
!=activeWindow
)
882 GetWindowClass( fl_xid( window
), &winClass
);
883 if ( winClass
!= kHelpWindowClass
) { // help windows can't get the focus!
884 Fl::handle( FL_FOCUS
, window
);
885 activeWindow
= window
;
889 case kEventWindowDeactivated
:
890 if ( window
==activeWindow
)
892 Fl::handle( FL_UNFOCUS
, window
);
896 case kEventWindowClose
:
897 Fl::handle( FL_CLOSE
, window
); // this might or might not close the window
898 // if there are no more windows, send a high-level quit event
899 if (!Fl_X::first
) QuitAppleEventHandler( 0, 0, 0 );
900 ret
= noErr
; // returning noErr tells Carbon to stop following up on this event
902 case kEventWindowCollapsed
:
903 window
->clear_visible();
905 case kEventWindowExpanded
:
906 window
->set_visible();
910 fl_unlock_function();
917 * Carbon Mousewheel handler
918 * This needs to be linked into all new window event handlers
920 static pascal OSStatus
carbonMousewheelHandler( EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
922 // Handle the new "MightyMouse" mouse wheel events. Please, someone explain
923 // to me why Apple changed the API on this even though the current API
924 // supports two wheels just fine. Matthias,
928 Fl_Window
*window
= (Fl_Window
*)userData
;
929 if ( !window
->shown() )
931 fl_unlock_function();
934 Fl::first_window(window
);
936 EventMouseWheelAxis axis
;
937 GetEventParameter( event
, kEventParamMouseWheelAxis
, typeMouseWheelAxis
, NULL
, sizeof(EventMouseWheelAxis
), NULL
, &axis
);
939 GetEventParameter( event
, kEventParamMouseWheelDelta
, typeLongInteger
, NULL
, sizeof(long), NULL
, &delta
);
940 // fprintf(stderr, "axis=%d, delta=%d\n", axis, delta);
941 if ( axis
== kEventMouseWheelAxisX
) {
944 if ( Fl::e_dx
) Fl::handle( FL_MOUSEWHEEL
, window
);
945 } else if ( axis
== kEventMouseWheelAxisY
) {
948 if ( Fl::e_dy
) Fl::handle( FL_MOUSEWHEEL
, window
);
950 fl_unlock_function();
952 return eventNotHandledErr
;
955 fl_unlock_function();
962 * convert the current mouse chord into the FLTK modifier state
964 static void chord_to_e_state( UInt32 chord
)
966 static ulong state
[] =
968 0, FL_BUTTON1
, FL_BUTTON3
, FL_BUTTON1
|FL_BUTTON3
, FL_BUTTON2
,
969 FL_BUTTON2
|FL_BUTTON1
, FL_BUTTON2
|FL_BUTTON3
,
970 FL_BUTTON2
|FL_BUTTON1
|FL_BUTTON3
972 Fl::e_state
= ( Fl::e_state
& 0xff0000 ) | state
[ chord
& 0x07 ];
977 * Carbon Mouse Button Handler
979 static pascal OSStatus
carbonMouseHandler( EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
981 static int keysym
[] = { 0, FL_Button
+1, FL_Button
+3, FL_Button
+2 };
983 static char suppressed
= 0;
988 Fl_Window
*window
= (Fl_Window
*)userData
;
989 if ( !window
->shown() )
991 fl_unlock_function();
994 Fl::first_window(window
);
996 GetEventParameter( event
, kEventParamMouseLocation
, typeQDPoint
, NULL
, sizeof(Point
), NULL
, &pos
);
997 EventMouseButton btn
;
998 GetEventParameter( event
, kEventParamMouseButton
, typeMouseButton
, NULL
, sizeof(EventMouseButton
), NULL
, &btn
);
1000 GetEventParameter( event
, kEventParamClickCount
, typeUInt32
, NULL
, sizeof(UInt32
), NULL
, &clickCount
);
1002 GetEventParameter( event
, kEventParamMouseChord
, typeUInt32
, NULL
, sizeof(UInt32
), NULL
, &chord
);
1003 WindowRef xid
= fl_xid(window
), tempXid
;
1004 int sendEvent
= 0, part
= 0;
1005 switch ( GetEventKind( event
) )
1007 case kEventMouseDown
:
1008 part
= FindWindow( pos
, &tempXid
);
1009 if (!(Fl::grab() && window
!=Fl::grab())) {
1010 if ( part
== inGrow
) {
1011 fl_unlock_function();
1013 Fl_Tooltip::current(0L);
1014 return CallNextEventHandler( nextHandler
, event
); // let the OS handle this for us
1016 if ( part
!= inContent
) {
1017 fl_unlock_function();
1019 Fl_Tooltip::current(0L);
1020 // anything else to here?
1021 return CallNextEventHandler( nextHandler
, event
); // let the OS handle this for us
1025 if (part
==inContent
&& !IsWindowActive( xid
) ) {
1026 CallNextEventHandler( nextHandler
, event
); // let the OS handle the activation, but continue to get a click-through effect
1028 // normal handling of mouse-down follows
1029 fl_os_capture
= xid
;
1030 sendEvent
= FL_PUSH
;
1031 Fl::e_is_click
= 1; px
= pos
.h
; py
= pos
.v
;
1042 if ( !window
) break;
1044 sendEvent
= FL_RELEASE
;
1046 Fl::e_keysym
= keysym
[ btn
];
1048 case kEventMouseMoved
:
1051 sendEvent
= FL_MOVE
; chord
= 0;
1054 case kEventMouseDragged
:
1055 if (suppressed
) break;
1057 sendEvent
= FL_MOVE
; // Fl::handle will convert into FL_DRAG
1058 if (abs(pos
.h
-px
)>5 || abs(pos
.v
-py
)>5)
1061 chord_to_e_state( chord
);
1063 GetPort( &oldPort
);
1064 SetPort( GetWindowPort(xid
) ); // \todo replace this! There must be some GlobalToLocal call that has a port as an argument
1066 Fl::e_x_root
= pos
.h
;
1067 Fl::e_y_root
= pos
.v
;
1068 GlobalToLocal( &pos
);
1072 if (GetEventKind(event
)==kEventMouseDown
&& part
!=inContent
) {
1073 int used
= Fl::handle( sendEvent
, window
);
1074 CallNextEventHandler( nextHandler
, event
); // let the OS handle this for us
1078 Fl::handle( sendEvent
, window
);
1083 fl_unlock_function();
1090 * convert the keyboard return code into the symbol on the keycaps
1092 static unsigned short keycode_to_sym( UInt32 keyCode
, UInt32 mods
, unsigned short deflt
)
1097 map
= (Ptr
)GetScriptManagerVariable(smKCHRCache
);
1099 long kbID
= GetScriptManagerVariable(smKeyScript
);
1100 map
= *GetResource('KCHR', kbID
);
1104 return KeyTranslate(map
, keyCode
|mods
, &state
);
1109 * keycode_function for post-10.5 systems, allows more sophisticated decoding of keys
1111 static int keycodeToUnicode(
1112 char * uniChars
, int maxChars
,
1114 UInt32 keycode
, UInt32 modifiers
,
1115 UInt32
* deadKeyStatePtr
,
1116 unsigned char, // not used in this function
1117 unsigned short) // not used in this function
1119 // first get the keyboard mapping in a post 10.2 way
1122 TextEncoding encoding
;
1123 static TextEncoding lastEncoding
= kTextEncodingMacRoman
;
1125 KeyboardLayoutRef currentLayout
= NULL
;
1126 static KeyboardLayoutRef lastLayout
= NULL
;
1127 SInt32 currentLayoutId
= 0;
1128 static SInt32 lastLayoutId
;
1129 int hasLayoutChanged
= false;
1130 static Ptr uchr
= NULL
;
1131 static Ptr KCHR
= NULL
;
1132 // ScriptCode currentKeyScript;
1134 KLGetCurrentKeyboardLayout(¤tLayout
);
1135 if (currentLayout
) {
1136 KLGetKeyboardLayoutProperty(currentLayout
, kKLIdentifier
, (const void**)¤tLayoutId
);
1137 if ( (lastLayout
!= currentLayout
) || (lastLayoutId
!= currentLayoutId
) ) {
1138 lastLayout
= currentLayout
;
1139 lastLayoutId
= currentLayoutId
;
1142 if ((KLGetKeyboardLayoutProperty(currentLayout
, kKLuchrData
, (const void**)&uchr
) == noErr
) && (uchr
!= NULL
)) {
1144 } else if ((KLGetKeyboardLayoutProperty(currentLayout
, kKLKCHRData
, (const void**)&KCHR
) == noErr
) && (KCHR
!= NULL
)) {
1147 // FIXME No Layout property found. Now we have a problem.
1150 if (hasLayoutChanged
) {
1151 //deadKeyStateUp = deadKeyStateDown = 0;
1153 // FIXME this must not happen
1154 } else if (uchr
== NULL
) {
1155 KCHR
= (Ptr
) GetScriptManagerVariable(smKCHRCache
);
1159 // this is what I expect
1163 encoding
= lastEncoding
;
1164 // this is actually not supported by the following code and will likely crash
1167 // now apply that keyboard mapping to our keycode
1170 //OptionBits options = 0;
1171 // not used yet: OptionBits options = kUCKeyTranslateNoDeadKeysMask;
1172 unsigned long keyboardType
;
1174 modifiers
= (modifiers
>> 8) & 0xFF;
1175 keyboardType
= LMGetKbdType();
1177 UniCharCount actuallength
;
1181 case kEventRawKeyDown
: action
= kUCKeyActionDown
; break;
1182 case kEventRawKeyUp
: action
= kUCKeyActionUp
; break;
1183 case kEventRawKeyRepeat
: action
= kUCKeyActionAutoKey
; break;
1187 UInt32 deadKeyState
= *deadKeyStatePtr
;
1188 if ((action
==kUCKeyActionUp
)&&(*deadKeyStatePtr
))
1189 deadKeyStatePtr
= &deadKeyState
;
1191 status
= UCKeyTranslate(
1192 (const UCKeyboardLayout
*) uchr
,
1193 keycode
, action
, modifiers
, keyboardType
,
1195 10, &actuallength
, utext
);
1197 if (noErr
!= status
) {
1198 fprintf(stderr
,"UCKeyTranslate failed: %d\n", (int) status
);
1202 // convert the list of unicode chars into utf8
1203 // FIXME no bounds check (see maxchars)
1205 for (i
=0; i
<actuallength
; ++i
) {
1206 len
+= fl_utf8encode(utext
[i
], uniChars
+len
);
1213 * keycode_function for pre-10.5 systems, this is the "historic" fltk Mac key handling
1215 static int keycode_wrap_old(
1217 int, EventKind
, UInt32
, // not used in this function
1218 UInt32
, UInt32
*, // not used in this function
1222 if ( (sym
>= FL_KP
&& sym
<= FL_KP_Last
) || !(sym
& 0xff00) ||
1223 sym
== FL_Tab
|| sym
== FL_Enter
) {
1230 } /* keycode_wrap_old */
1232 * Stub pointer to select appropriate keycode_function per operating system version. This function pointer
1233 * is initialised in fl_open_display, based on the runtime identification of the host OS version. This is
1234 * intended to allow us to utilise 10.5 services dynamically to improve Unicode handling, whilst still
1235 * allowing code to run satisfactorily on older systems.
1237 static int (*keycode_function
)(char*, int, EventKind
, UInt32
, UInt32
, UInt32
*, unsigned char, unsigned short) = keycode_wrap_old
;
1241 pascal OSStatus
carbonTextHandler(
1242 EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
1244 Fl_Window
*window
= (Fl_Window
*)userData
;
1245 Fl::first_window(window
);
1247 //int kind = GetEventKind(event);
1248 unsigned short buf
[200];
1250 GetEventParameter( event
, kEventParamTextInputSendText
, typeUnicodeText
,
1251 NULL
, 100, &size
, &buf
);
1252 // printf("TextEvent: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);
1253 // FIXME: oversimplified!
1254 unsigned ucs
= buf
[0];
1256 int len
= fl_utf8encode(ucs
, utf8buf
);
1258 Fl::e_text
= utf8buf
;
1259 while (window
->parent()) window
= window
->window();
1260 Fl::handle(FL_KEYBOARD
, window
);
1261 fl_unlock_function();
1263 Fl::handle(FL_KEYUP
, window
);
1264 fl_unlock_function();
1265 // for some reason, the window does not redraw until the next mouse move or button push
1266 // sending a 'redraw()' or 'awake()' does not solve the issue!
1272 * handle carbon keyboard events
1274 pascal OSStatus
carbonKeyboardHandler(
1275 EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
1277 static char buffer
[32];
1279 Fl_Window
*window
= (Fl_Window
*)userData
;
1280 Fl::first_window(window
);
1282 static UInt32 prevMods
= mods_to_e_state( GetCurrentKeyModifiers() );
1286 int kind
= GetEventKind(event
);
1288 // get the modifiers for any of the events
1289 GetEventParameter( event
, kEventParamKeyModifiers
, typeUInt32
,
1290 NULL
, sizeof(UInt32
), NULL
, &mods
);
1292 // get the key code only for key events
1293 UInt32 keyCode
= 0, maskedKeyCode
= 0;
1294 unsigned char key
= 0;
1295 unsigned short sym
= 0;
1296 if (kind
!=kEventRawKeyModifiersChanged
) {
1297 GetEventParameter( event
, kEventParamKeyCode
, typeUInt32
,
1298 NULL
, sizeof(UInt32
), NULL
, &keyCode
);
1299 GetEventParameter( event
, kEventParamKeyMacCharCodes
, typeChar
,
1300 NULL
, sizeof(char), NULL
, &key
);
1302 // extended keyboards can also send sequences on key-up to generate Kanji etc. codes.
1303 // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode.
1304 // In this mode, there seem to be no key-down codes
1305 // printf("%08x %08x %08x\n", keyCode, mods, key);
1306 maskedKeyCode
= keyCode
& 0x7f;
1307 /* output a human readable event identifier for debugging
1308 const char *ev = "";
1310 case kEventRawKeyDown: ev = "kEventRawKeyDown"; break;
1311 case kEventRawKeyRepeat: ev = "kEventRawKeyRepeat"; break;
1312 case kEventRawKeyUp: ev = "kEventRawKeyUp"; break;
1313 case kEventRawKeyModifiersChanged: ev = "kEventRawKeyModifiersChanged"; break;
1314 default: ev = "unknown";
1316 printf("%08x %08x %08x '%c' %s \n", mods, keyCode, key, key, ev);
1320 case kEventRawKeyDown
:
1321 case kEventRawKeyRepeat
:
1323 // FIXME Matt: For 10.5, the keycode_function will handle all this. This is untested for ealier versions of OS X.
1324 // When the user presses a "dead key", no information is send about
1325 // which dead key symbol was created. So we need to trick Carbon into
1326 // giving us the code by sending a "space" after the "dead key".
1329 KeyboardLayoutRef klr;
1330 KLGetCurrentKeyboardLayout(&klr);
1331 const void *kchar = 0; KLGetKeyboardLayoutProperty(klr, kKLKCHRData, &kchar);
1332 KeyTranslate(kchar, (mods&0xff00) | keyCode, &ktState); // send the dead key
1333 key = KeyTranslate(kchar, 0x31, &ktState); // fake a space key press
1334 Fl::e_state |= 0x40000000; // mark this as a dead key
1336 Fl::e_state &= 0xbfffffff; // clear the deadkey flag
1339 sendEvent
= FL_KEYBOARD
;
1341 case kEventRawKeyUp
:
1343 sendEvent
= FL_KEYUP
;
1344 Fl::e_state
&= 0xbfffffff; // clear the deadkey flag
1346 // if the user pressed alt/option, event_key should have the keycap,
1347 // but event_text should generate the international symbol
1348 sym
= macKeyLookUp
[maskedKeyCode
];
1351 else if ( Fl::e_state
&FL_CTRL
&& key
<32 && sym
<0xff00)
1353 else if ( Fl::e_state
&FL_ALT
&& sym
<0xff00) // find the keycap of this key
1354 sym
= keycode_to_sym( maskedKeyCode
, 0, macKeyLookUp
[ maskedKeyCode
] );
1355 Fl::e_keysym
= Fl::e_original_keysym
= sym
;
1356 // Handle FL_KP_Enter on regular keyboards and on Powerbooks
1357 if ( maskedKeyCode
==0x4c || maskedKeyCode
==0x34) key
=0x0d;
1358 // Handle the Delete key on the keypad
1359 // Matt: the Mac has no concept of a NumLock key, or at least not visible
1360 // Matt: to Carbon. The kEventKeyModifierNumLockMask is only set when
1361 // Matt: a numeric keypad key is pressed and does not correspond with
1362 // Matt: the NumLock light in PowerBook keyboards.
1364 // Matt: attempt to get the correct Unicode character(s) from our keycode
1365 // imm: keycode_function function pointer added to allow us to use different functions
1366 // imm: depending on which OS version we are running on (tested and set in fl_open_display)
1367 static UInt32 deadKeyState
= 0; // must be cleared when losing focus
1368 Fl::e_length
= (*keycode_function
)(buffer
, 31, kind
, keyCode
, mods
, &deadKeyState
, key
, sym
);
1369 Fl::e_text
= buffer
;
1370 buffer
[Fl::e_length
] = 0; // just in case...
1372 case kEventRawKeyModifiersChanged
: {
1373 UInt32 tMods
= prevMods
^ mods
;
1376 mods_to_e_keysym( tMods
);
1378 sendEvent
= ( prevMods
<mods
) ? FL_KEYBOARD
: FL_KEYUP
;
1383 mods_to_e_state( mods
);
1386 while (window
->parent()) window
= window
->window();
1387 if (sendEvent
&& Fl::handle(sendEvent
,window
)) {
1388 fl_unlock_function();
1389 return noErr
; // return noErr if FLTK handled the event
1391 fl_unlock_function();
1392 //return CallNextEventHandler( nextHandler, event );;
1393 // Matt: I had better results (no duplicate events) always returning
1394 // Matt: 'noErr'. System keyboard events still seem to work just fine.
1402 * Open callback function to call...
1405 static void (*open_cb
)(const char *) = 0;
1409 * Event handler for Apple-O key combination and also for file opens
1413 static OSErr
OpenAppleEventHandler(const AppleEvent
*appleEvt
,
1417 AEDescList documents
;
1423 char filename
[1024];
1425 if (!open_cb
) return noErr
;
1427 // Initialize the document list...
1428 AECreateDesc(typeNull
, NULL
, 0, &documents
);
1430 // Get the open parameter(s)...
1431 err
= AEGetParamDesc(appleEvt
, keyDirectObject
, typeAEList
, &documents
);
1433 AEDisposeDesc(&documents
);
1437 // Lock access to FLTK in this thread...
1440 // Open the documents via the callback...
1441 if (AECountItems(&documents
, &n
) == noErr
) {
1442 for (i
= 1; i
<= n
; i
++) {
1443 // Get the next FSSpec record...
1444 AEGetNthPtr(&documents
, i
, typeFSS
, &keyWd
, &typeCd
,
1445 (Ptr
)&fileSpec
, sizeof(fileSpec
),
1446 (actSz
= sizeof(fileSpec
), &actSz
));
1448 // Convert to a UNIX path...
1449 FSSpec2UnixPath(&fileSpec
, filename
);
1451 // Call the callback with the filename...
1452 (*open_cb
)(filename
);
1456 // Unlock access to FLTK for all threads...
1457 fl_unlock_function();
1459 // Get rid of the document list...
1460 AEDisposeDesc(&documents
);
1467 * Install an open documents event handler...
1470 void fl_open_callback(void (*cb
)(const char *)) {
1473 AEInstallEventHandler(kCoreEventClass
, kAEOpenDocuments
,
1474 NewAEEventHandlerUPP((AEEventHandlerProcPtr
)
1475 OpenAppleEventHandler
), 0, false);
1477 AERemoveEventHandler(kCoreEventClass
, kAEOpenDocuments
,
1478 NewAEEventHandlerUPP((AEEventHandlerProcPtr
)
1479 OpenAppleEventHandler
), false);
1485 * initialize the Mac toolboxes, dock status, and set the default menubar
1489 extern OSErr
CPSEnableForegroundOperation(ProcessSerialNumber
*psn
, UInt32 _arg2
,
1490 UInt32 _arg3
, UInt32 _arg4
, UInt32 _arg5
);
1493 void fl_open_display() {
1494 static char beenHereDoneThat
= 0;
1495 if ( !beenHereDoneThat
) {
1496 beenHereDoneThat
= 1;
1498 FlushEvents(everyEvent
,0);
1500 MoreMasters(); // \todo Carbon suggests MoreMasterPointers()
1501 AEInstallEventHandler( kCoreEventClass
, kAEQuitApplication
, NewAEEventHandlerUPP((AEEventHandlerProcPtr
)QuitAppleEventHandler
), 0, false );
1503 // create the Mac Handle for the default cursor (a pointer to a pointer)
1504 GetQDGlobalsArrow(&default_cursor
);
1505 default_cursor_ptr
= &default_cursor
;
1506 fl_default_cursor
= &default_cursor_ptr
;
1509 AppendResMenu( GetMenuHandle( 1 ), 'DRVR' );
1512 // bring the application into foreground without a 'CARB' resource
1514 ProcessSerialNumber cur_psn
, front_psn
;
1515 if( !GetCurrentProcess( &cur_psn
) && !GetFrontProcess( &front_psn
) &&
1516 !SameProcess( &front_psn
, &cur_psn
, &same_psn
) && !same_psn
)
1518 // only transform the application type for unbundled apps
1519 CFBundleRef bundle
= CFBundleGetMainBundle();
1523 CFURLRef execUrl
= CFBundleCopyExecutableURL( bundle
);
1524 CFURLGetFSRef( execUrl
, &execFs
);
1527 GetProcessBundleLocation( &cur_psn
, &bundleFs
);
1529 if( !FSCompareFSRefs( &execFs
, &bundleFs
) )
1537 // Earlier versions of this code tried to use weak linking, however it
1538 // appears that this does not work on 10.2. Since 10.3 and higher provide
1539 // both TransformProcessType and CPSEnableForegroundOperation, the following
1540 // conditional code compiled on 10.2 will still work on newer releases...
1543 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1544 if (TransformProcessType
!= NULL
) {
1545 err
= TransformProcessType(&cur_psn
, kProcessTransformToForegroundApplication
);
1547 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1548 err
= CPSEnableForegroundOperation(&cur_psn
, 0x03, 0x3C, 0x2C, 0x1103);
1551 SetFrontProcess( &cur_psn
);
1556 // imm: keycode handler stub setting - use Gestalt to determine the running system version,
1557 // then set the keycode_function pointer accordingly
1558 keycode_function
= keycode_wrap_old
; // default to pre-10.5 mechanism
1560 if (Gestalt(gestaltSystemVersion
, &MacVersion
) == noErr
)
1562 if(MacVersion
>= 0x1050) { // 10.5.0 or later
1563 keycode_function
= keycodeToUnicode
;
1571 * get rid of allocated resources
1573 void fl_close_display() {
1578 * smallest x ccordinate in screen space
1582 GetQDGlobalsScreenBits(&r
);
1583 return r
.bounds
.left
;
1588 * smallest y ccordinate in screen space
1592 GetQDGlobalsScreenBits(&r
);
1593 return r
.bounds
.top
+ 20; // \todo 20 pixel menu bar?
1598 * screen width (single monitor!?)
1602 GetQDGlobalsScreenBits(&r
);
1603 return r
.bounds
.right
- r
.bounds
.left
;
1608 * screen height (single monitor!?)
1612 GetQDGlobalsScreenBits(&r
);
1613 return r
.bounds
.bottom
- r
.bounds
.top
- 20;
1618 * get the current mouse pointer world coordinates
1620 void Fl::get_mouse(int &x
, int &y
)
1625 LocalToGlobal( &loc
);
1632 * convert Mac keystrokes to FLTK
1634 unsigned short mac2fltk(ulong macKey
)
1636 unsigned short cc
= macKeyLookUp
[(macKey
>>8)&0x7f];
1643 * Initialize the given port for redraw and call the windw's flush() to actually draw the content
1649 CGContextFlush(fl_gc
);
1655 * Handle all clipping and redraw for the given port
1656 * There are two different callers for this event:
1657 * 1: the OS can request a redraw and provides all clipping itself
1658 * 2: Fl::flush() wants all redraws now
1660 void handleUpdateEvent( WindowPtr xid
)
1662 Fl_Window
*window
= fl_find( xid
);
1663 if ( !window
) return;
1665 GetPort( &oldPort
);
1666 SetPort( GetWindowPort(xid
) );
1667 Fl_X
*i
= Fl_X::i( window
);
1668 i
->wait_for_expose
= 0;
1669 if ( window
->damage() ) {
1671 InvalWindowRgn( xid
, i
->region
);
1674 if ( i
->region
) { // no region, so the sytem will take the update region from the OS
1675 DisposeRgn( i
->region
);
1678 for ( Fl_X
*cx
= i
->xidChildren
; cx
; cx
= cx
->xidNext
)
1680 cx
->w
->clear_damage(window
->damage()|FL_DAMAGE_EXPOSE
);
1682 cx
->w
->clear_damage();
1684 window
->clear_damage(window
->damage()|FL_DAMAGE_EXPOSE
);
1686 window
->clear_damage();
1690 // Gets the border sizes and the titlebar size
1691 static void get_window_frame_sizes(int &bx
, int &by
, int &bt
) {
1692 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1693 static HIRect contentRect
= { {50,50}, {100,100} }; // a rect to stand in for the content rect of a real window
1694 static HIThemeWindowDrawInfo metrics
= {0,
1695 kThemeStateActive
, kThemeDocumentWindow
,
1696 kThemeWindowHasFullZoom
+ kThemeWindowHasCloseBox
+
1697 kThemeWindowHasCollapseBox
+ kThemeWindowHasTitleText
,
1699 HIShapeRef shape1
=0, shape2
=0, shape3
=0;
1700 HIRect rect1
, rect2
, rect3
;
1702 status
= HIThemeGetWindowShape(&contentRect
, &metrics
, kWindowStructureRgn
, &shape1
);
1703 status
|= HIThemeGetWindowShape(&contentRect
, &metrics
, kWindowContentRgn
, &shape2
);
1704 status
|= HIThemeGetWindowShape(&contentRect
, &metrics
, kWindowTitleBarRgn
, &shape3
);
1708 HIShapeGetBounds(shape1
, &rect1
);
1709 HIShapeGetBounds(shape2
, &rect2
);
1710 HIShapeGetBounds(shape3
, &rect3
);
1711 bt
= rect3
.size
.height
;
1712 bx
= rect2
.origin
.x
- rect1
.origin
.x
;
1713 by
= rect2
.origin
.y
- rect1
.origin
.y
- bt
;
1714 // fprintf(stderr, "HIThemeGetWindowShape succeeded bx=%d by=%d bt=%d\n", bx, by, bt);
1717 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1719 // sets default dimensions
1722 // fprintf(stderr, "HIThemeGetWindowShape failed, bx=%d by=%d bt=%d\n", bx, by, bt);
1724 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1725 CFRelease(shape1
); // we must free HIThemeGetWindowShape() (copied) handles
1728 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
1732 * \todo this is a leftover from OS9 times. Please check how much applies to Carbon!
1734 int Fl_X::fake_X_wm(const Fl_Window
* w
,int &X
,int &Y
, int &bt
,int &bx
, int &by
) {
1735 int W
, H
, xoff
, yoff
, dx
, dy
;
1736 int ret
= bx
= by
= bt
= 0;
1737 if (w
->border() && !w
->parent()) {
1738 if (w
->maxw
!= w
->minw
|| w
->maxh
!= w
->minh
) {
1740 get_window_frame_sizes(bx
, by
, bt
);
1742 bx = 6; // \todo Mac : GetSystemMetrics(SM_CXSIZEFRAME);
1743 by = 6; // \todo Mac : get Mac window frame size GetSystemMetrics(SM_CYSIZEFRAME);
1747 get_window_frame_sizes(bx
, by
, bt
);
1749 bx = 6; // \todo Mac : GetSystemMetrics(SM_CXFIXEDFRAME);
1750 by = 6; // \todo Mac : GetSystemMetrics(SM_CYFIXEDFRAME);
1754 //The coordinates of the whole window, including non-client area
1764 //Proceed to positioning the window fully inside the screen, if possible
1766 // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk
1767 // that we want to avoid when positioning our window, namely the Dock and the
1768 // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the
1769 // list of all available screens and find the one that this window is most
1770 // likely to go to, and then reposition it to fit withing the 'good' area.
1772 // find the screen, that the center of this window will fall into
1773 int R
= X
+W
, B
= Y
+H
; // right and bottom
1774 int cx
= (X
+R
)/2, cy
= (Y
+B
)/2; // center of window;
1776 for (gd
= GetDeviceList(); gd
; gd
= GetNextDevice(gd
)) {
1778 if ( cx
>= gp
->gdRect
.left
&& cx
<= gp
->gdRect
.right
1779 && cy
>= gp
->gdRect
.top
&& cy
<= gp
->gdRect
.bottom
)
1782 // if the center doesn't fall on a screen, try the top left
1784 for (gd
= GetDeviceList(); gd
; gd
= GetNextDevice(gd
)) {
1786 if ( X
>= gp
->gdRect
.left
&& X
<= gp
->gdRect
.right
1787 && Y
>= gp
->gdRect
.top
&& Y
<= gp
->gdRect
.bottom
)
1791 // if that doesn't fall on a screen, try the top right
1793 for (gd
= GetDeviceList(); gd
; gd
= GetNextDevice(gd
)) {
1795 if ( R
>= gp
->gdRect
.left
&& R
<= gp
->gdRect
.right
1796 && Y
>= gp
->gdRect
.top
&& Y
<= gp
->gdRect
.bottom
)
1800 // if that doesn't fall on a screen, try the bottom left
1802 for (gd
= GetDeviceList(); gd
; gd
= GetNextDevice(gd
)) {
1804 if ( X
>= gp
->gdRect
.left
&& X
<= gp
->gdRect
.right
1805 && B
>= gp
->gdRect
.top
&& B
<= gp
->gdRect
.bottom
)
1809 // last resort, try the bottom right
1811 for (gd
= GetDeviceList(); gd
; gd
= GetNextDevice(gd
)) {
1813 if ( R
>= gp
->gdRect
.left
&& R
<= gp
->gdRect
.right
1814 && B
>= gp
->gdRect
.top
&& B
<= gp
->gdRect
.bottom
)
1818 // if we still have not found a screen, we will use the main
1819 // screen, the one that has the application menu bar.
1820 if (!gd
) gd
= GetMainDevice();
1822 GetAvailableWindowPositioningBounds(gd
, &r
);
1823 if ( R
> r
.right
) X
-= R
- r
.right
;
1824 if ( B
> r
.bottom
) Y
-= B
- r
.bottom
;
1825 if ( X
< r
.left
) X
= r
.left
;
1826 if ( Y
< r
.top
) Y
= r
.top
;
1829 //Return the client area's top left corner in (X,Y)
1837 * convert a Mac FSSpec structure into a Unix filename
1839 static int FSSpec2UnixPath( FSSpec
*fs
, char *dst
)
1842 FSpMakeFSRef( fs
, &fsRef
);
1843 FSRefMakePath( &fsRef
, (UInt8
*)dst
, 1024 );
1846 static void convert_crlf(char * s
, size_t len
)
1848 // turn all \r characters into \n:
1849 for (size_t x
= 0; x
< len
; x
++) if (s
[x
] == '\r') s
[x
] = '\n';
1853 static DragReference currDragRef
= 0;
1854 static char *currDragData
= 0L;
1855 static int currDragSize
= 0;
1856 static OSErr currDragErr
= noErr
;
1857 Fl_Window
*fl_dnd_target_window
= 0;
1858 #include <FL/fl_draw.H>
1861 * Fill the currDrag* variables with the current DnD ASCII text.
1863 static OSErr
fillCurrentDragData(DragReference dragRef
)
1868 // shortcut through this whole procedure if this is still the same drag event
1869 if (dragRef
==currDragRef
)
1872 // clear currDrag* for a new drag event
1873 currDragRef
= dragRef
;
1874 if (currDragData
) free(currDragData
);
1878 // fill currDRag* with ASCII data, if available
1880 ItemReference itemRef
;
1882 Size itemSize
, size
= 0;
1883 CountDragItems( dragRef
, &nItem
);
1885 for ( i
= 1; i
<= nItem
; i
++ )
1887 GetDragItemReferenceNumber( dragRef
, i
, &itemRef
);
1888 ret
= GetFlavorFlags( dragRef
, itemRef
, 'utf8', &flags
);
1891 GetFlavorDataSize( dragRef
, itemRef
, 'utf8', &itemSize
);
1895 ret
= GetFlavorFlags( dragRef
, itemRef
, 'utxt', &flags
);
1898 GetFlavorDataSize( dragRef
, itemRef
, 'utxt', &itemSize
);
1902 ret
= GetFlavorFlags( dragRef
, itemRef
, 'TEXT', &flags
);
1905 GetFlavorDataSize( dragRef
, itemRef
, 'TEXT', &itemSize
);
1909 ret
= GetFlavorFlags( dragRef
, itemRef
, 'hfs ', &flags
);
1912 size
+= 1024; //++ ouch! We should create the full pathname and figure out its length
1919 currDragErr
= userCanceledErr
;
1923 currDragSize
= size
+ nItem
- 1;
1924 currDragData
= dst
= (char*)malloc( size
+nItem
);;
1926 for ( i
= 1; i
<= nItem
; i
++ )
1928 GetDragItemReferenceNumber( dragRef
, i
, &itemRef
);
1929 ret
= GetFlavorFlags( dragRef
, itemRef
, 'utf8', &flags
);
1932 GetFlavorDataSize( dragRef
, itemRef
, 'utf8', &itemSize
);
1933 GetFlavorData( dragRef
, itemRef
, 'utf8', dst
, &itemSize
, 0L );
1935 *dst
++ = '\n'; // add our element separator
1938 GetDragItemReferenceNumber( dragRef
, i
, &itemRef
);
1939 ret
= GetFlavorFlags( dragRef
, itemRef
, 'utxt', &flags
);
1942 GetFlavorDataSize( dragRef
, itemRef
, 'utxt', &itemSize
);
1943 GetFlavorData( dragRef
, itemRef
, 'utxt', dst
, &itemSize
, 0L );
1945 *dst
++ = '\n'; // add our element separator
1948 ret
= GetFlavorFlags( dragRef
, itemRef
, 'TEXT', &flags
);
1951 GetFlavorDataSize( dragRef
, itemRef
, 'TEXT', &itemSize
);
1952 GetFlavorData( dragRef
, itemRef
, 'TEXT', dst
, &itemSize
, 0L );
1954 *dst
++ = '\n'; // add our element separator
1957 ret
= GetFlavorFlags( dragRef
, itemRef
, 'hfs ', &flags
);
1960 HFSFlavor hfs
; itemSize
= sizeof( hfs
);
1961 GetFlavorData( dragRef
, itemRef
, 'hfs ', &hfs
, &itemSize
, 0L );
1962 itemSize
= FSSpec2UnixPath( &hfs
.fileSpec
, dst
); // return the path name in UTF8
1964 if ( itemSize
>1 && ( hfs
.fileType
=='fold' || hfs
.fileType
=='disk' ) )
1966 *dst
++ = '\n'; // add our element separator
1972 currDragSize
= dst
- currDragData
- 1;
1978 * Drag'n'drop tracking handler
1980 static pascal OSErr
dndTrackingHandler( DragTrackingMessage msg
, WindowPtr w
, void *userData
, DragReference dragRef
)
1982 Fl_Window
*target
= (Fl_Window
*)userData
;
1983 Fl::first_window(target
);
1987 fillCurrentDragData(dragRef
);
1988 Fl::e_length
= currDragSize
;
1989 Fl::e_text
= currDragData
;
1993 case kDragTrackingEnterWindow
:
1994 // check if 'TEXT' is available
1995 GetDragMouse( dragRef
, &mp
, 0 );
1996 Fl::e_x_root
= px
= mp
.h
;
1997 Fl::e_y_root
= py
= mp
.v
;
1998 Fl::e_x
= px
- target
->x();
1999 Fl::e_y
= py
- target
->y();
2000 fl_dnd_target_window
= target
;
2001 if ( Fl::handle( FL_DND_ENTER
, target
) )
2002 fl_cursor( FL_CURSOR_HAND
); //ShowDragHilite( ); // modify the mouse cursor?!
2004 fl_cursor( FL_CURSOR_DEFAULT
); //HideDragHilite( dragRef );
2005 breakMacEventLoop();
2007 case kDragTrackingInWindow
:
2008 GetDragMouse( dragRef
, &mp
, 0 );
2009 if ( mp
.h
==px
&& mp
.v
==py
)
2010 break; //+ return previous condition for dnd hiliting
2011 Fl::e_x_root
= px
= mp
.h
;
2012 Fl::e_y_root
= py
= mp
.v
;
2013 Fl::e_x
= px
- target
->x();
2014 Fl::e_y
= py
- target
->y();
2015 fl_dnd_target_window
= target
;
2016 if ( Fl::handle( FL_DND_DRAG
, target
) )
2017 fl_cursor( FL_CURSOR_HAND
); //ShowDragHilite( ); // modify the mouse cursor?!
2019 fl_cursor( FL_CURSOR_DEFAULT
); //HideDragHilite( dragRef );
2020 breakMacEventLoop();
2023 case kDragTrackingLeaveWindow
:
2025 fl_cursor( FL_CURSOR_DEFAULT
); //HideDragHilite( dragRef );
2026 if ( fl_dnd_target_window
)
2028 Fl::handle( FL_DND_LEAVE
, fl_dnd_target_window
);
2029 fl_dnd_target_window
= 0;
2031 breakMacEventLoop();
2039 * Drag'n'drop receive handler
2041 static pascal OSErr
dndReceiveHandler( WindowPtr w
, void *userData
, DragReference dragRef
)
2046 Fl_Window
*target
= fl_dnd_target_window
= (Fl_Window
*)userData
;
2047 Fl::first_window(target
);
2048 GetDragMouse( dragRef
, &mp
, 0 );
2049 Fl::e_x_root
= mp
.h
;
2050 Fl::e_y_root
= mp
.v
;
2051 Fl::e_x
= Fl::e_x_root
- target
->x();
2052 Fl::e_y
= Fl::e_y_root
- target
->y();
2053 if ( !Fl::handle( FL_DND_RELEASE
, target
) )
2054 return userCanceledErr
;
2056 ret
= fillCurrentDragData(dragRef
);
2057 if (ret
==userCanceledErr
)
2058 return userCanceledErr
;
2060 Fl::e_length
= currDragSize
;
2061 Fl::e_text
= currDragData
;
2062 // printf("Sending following text to widget %p:\n%s\n", Fl::belowmouse(), Fl::e_text);
2063 int old_event
= Fl::e_number
;
2064 Fl::belowmouse()->handle(Fl::e_number
= FL_PASTE
);
2065 Fl::e_number
= old_event
;
2074 fl_dnd_target_window
= 0L;
2076 breakMacEventLoop();
2080 static void q_set_window_title(Window xid
, const char * name
) {
2082 CFStringRef utf8_title
= CFStringCreateWithCString(NULL
, (name
? name
: ""), kCFStringEncodingUTF8
);
2083 SetWindowTitleWithCFString(xid
, utf8_title
);
2084 CFRelease(utf8_title
);
2085 #else // old non-utf8 code to remove after new utf8 code approval :
2088 if (strlen(name
) > 255) pTitle
[0] = 255;
2089 else pTitle
[0] = strlen(name
);
2090 memcpy(pTitle
+1, name
, pTitle
[0]);
2094 SetWTitle(xid
, pTitle
);
2099 * go ahead, create that (sub)window
2100 * \todo we should make menu windows slightly transparent for the new Mac look
2102 void Fl_X::make(Fl_Window
* w
)
2104 static int xyPos
= 100;
2105 if ( w
->parent() ) // create a subwindow
2107 Fl_Group::current(0);
2110 wRect
.left
= w
->x();
2111 wRect
.bottom
= w
->y() + w
->h(); if (wRect
.bottom
<=wRect
.top
) wRect
.bottom
= wRect
.top
+1;
2112 wRect
.right
= w
->x() + w
->w(); if (wRect
.right
<=wRect
.left
) wRect
.right
= wRect
.left
+1;
2113 // our subwindow needs this structure to know about its clipping.
2118 x
->cursor
= fl_default_cursor
;
2119 x
->gc
= 0; // stay 0 for Quickdraw; fill with CGContext for Quartz
2120 Fl_Window
*win
= w
->window();
2121 Fl_X
*xo
= Fl_X::i(win
);
2123 x
->xidNext
= xo
->xidChildren
;
2124 x
->xidChildren
= 0L;
2125 xo
->xidChildren
= x
;
2126 x
->xid
= fl_xid(win
);
2128 x
->wait_for_expose
= 0;
2129 x
->next
= Fl_X::first
; // must be in the list for ::flush()
2131 int old_event
= Fl::e_number
;
2132 w
->handle(Fl::e_number
= FL_SHOW
);
2133 Fl::e_number
= old_event
;
2134 w
->redraw(); // force draw to happen
2138 else // create a desktop window
2140 Fl_Group::current(0);
2142 int winclass
= kDocumentWindowClass
;
2143 int winattr
= kWindowStandardHandlerAttribute
| kWindowCloseBoxAttribute
| kWindowCollapseBoxAttribute
;
2148 if (w
->size_range_set
) {
2149 if ( w
->minh
!= w
->maxh
|| w
->minw
!= w
->maxw
)
2150 winattr
|= kWindowFullZoomAttribute
| kWindowResizableAttribute
| kWindowLiveResizeAttribute
;
2152 if (w
->resizable()) {
2153 Fl_Widget
*o
= w
->resizable();
2154 int minw
= o
->w(); if (minw
> 100) minw
= 100;
2155 int minh
= o
->h(); if (minh
> 100) minh
= 100;
2156 w
->size_range(w
->w() - o
->w() + minw
, w
->h() - o
->h() + minh
, 0, 0);
2157 winattr
|= kWindowFullZoomAttribute
| kWindowResizableAttribute
| kWindowLiveResizeAttribute
;
2159 w
->size_range(w
->w(), w
->h(), w
->w(), w
->h());
2162 int xwm
= xp
, ywm
= yp
, bt
, bx
, by
;
2164 if (!fake_X_wm(w
, xwm
, ywm
, bt
, bx
, by
)) {
2165 // menu windows and tooltips
2166 if (w
->modal()||w
->override()) {
2167 winclass
= kHelpWindowClass
;
2170 winattr
= 512; // kWindowNoTitleBarAttribute;
2172 } else if (w
->modal()) {
2173 winclass
= kMovableModalWindowClass
;
2180 if (!(w
->flags() & Fl_Widget::FORCE_POSITION
)) {
2181 // use the Carbon functions below for default window positioning
2182 w
->x(xyPos
+Fl::x());
2183 w
->y(xyPos
+Fl::y());
2185 if (xyPos
>200) xyPos
= 100;
2195 if (w
->non_modal() && Fl_X::first
&& !fl_disable_transient_for
) {
2196 // find some other window to be "transient for":
2197 Fl_Window
* w
= Fl_X::first
->w
;
2198 while (w
->parent()) w
= w
->window(); // todo: this code does not make any sense! (w!=w??)
2203 wRect
.left
= w
->x();
2204 wRect
.bottom
= w
->y() + w
->h(); if (wRect
.bottom
<=wRect
.top
) wRect
.bottom
= wRect
.top
+1;
2205 wRect
.right
= w
->x() + w
->w(); if (wRect
.right
<=wRect
.left
) wRect
.right
= wRect
.left
+1;
2207 const char *name
= w
->label();
2210 x
->other_xid
= 0; // room for doublebuffering image map. On OS X this is only used by overlay windows
2213 x
->cursor
= fl_default_cursor
;
2218 winattr
&= GetAvailableWindowAttributes( winclass
); // make sure that the window will open
2219 CreateNewWindow( winclass
, winattr
, &wRect
, &(x
->xid
) );
2220 q_set_window_title(x
->xid
, name
);
2221 MoveWindow(x
->xid
, wRect
.left
, wRect
.top
, 1); // avoid Carbon Bug on old OS
2222 if (w
->non_modal() && !w
->modal()) {
2223 // Major kludge: this is to have the regular look, but stay above the document windows
2224 SetWindowClass(x
->xid
, kFloatingWindowClass
);
2225 SetWindowActivationScope(x
->xid
, kWindowActivationScopeAll
);
2227 if (!(w
->flags() & Fl_Widget::FORCE_POSITION
))
2229 WindowRef pw
= Fl_X::first
? Fl_X::first
->xid
: 0 ;
2231 RepositionWindow(x
->xid
, pw
, kWindowAlertPositionOnParentWindowScreen
);
2232 } else if (w
->non_modal()) {
2233 RepositionWindow(x
->xid
, pw
, kWindowCenterOnParentWindowScreen
);
2235 RepositionWindow(x
->xid
, pw
, kWindowCascadeOnParentWindowScreen
);
2239 x
->wait_for_expose
= 1;
2240 x
->next
= Fl_X::first
;
2242 { // Install Carbon Event handlers
2244 EventHandlerUPP mousewheelHandler
= NewEventHandlerUPP( carbonMousewheelHandler
); // will not be disposed by Carbon...
2245 static EventTypeSpec mousewheelEvents
[] = {
2246 { kEventClassMouse
, kEventMouseWheelMoved
} };
2247 ret
= InstallWindowEventHandler( x
->xid
, mousewheelHandler
,
2248 (int)(sizeof(mousewheelEvents
)/sizeof(mousewheelEvents
[0])),
2249 mousewheelEvents
, w
, 0L );
2250 EventHandlerUPP mouseHandler
= NewEventHandlerUPP( carbonMouseHandler
); // will not be disposed by Carbon...
2251 static EventTypeSpec mouseEvents
[] = {
2252 { kEventClassMouse
, kEventMouseDown
},
2253 { kEventClassMouse
, kEventMouseUp
},
2254 { kEventClassMouse
, kEventMouseMoved
},
2255 { kEventClassMouse
, kEventMouseDragged
} };
2256 ret
= InstallWindowEventHandler( x
->xid
, mouseHandler
, 4, mouseEvents
, w
, 0L );
2258 EventHandlerUPP keyboardHandler
= NewEventHandlerUPP( carbonKeyboardHandler
); // will not be disposed by Carbon...
2259 static EventTypeSpec keyboardEvents
[] = {
2260 { kEventClassKeyboard
, kEventRawKeyDown
},
2261 { kEventClassKeyboard
, kEventRawKeyRepeat
},
2262 { kEventClassKeyboard
, kEventRawKeyUp
},
2263 { kEventClassKeyboard
, kEventRawKeyModifiersChanged
} };
2264 ret
= InstallWindowEventHandler( x
->xid
, keyboardHandler
, 4, keyboardEvents
, w
, 0L );
2266 EventHandlerUPP textHandler
= NewEventHandlerUPP( carbonTextHandler
); // will not be disposed by Carbon...
2267 static EventTypeSpec textEvents
[] = {
2268 { kEventClassTextInput
, kEventTextInputUnicodeForKeyEvent
} };
2269 ret
= InstallWindowEventHandler( x
->xid
, textHandler
, 1, textEvents
, w
, 0L );
2271 EventHandlerUPP windowHandler
= NewEventHandlerUPP( carbonWindowHandler
); // will not be disposed by Carbon...
2272 static EventTypeSpec windowEvents
[] = {
2273 { kEventClassWindow
, kEventWindowDrawContent
},
2274 { kEventClassWindow
, kEventWindowShown
},
2275 { kEventClassWindow
, kEventWindowHidden
},
2276 { kEventClassWindow
, kEventWindowActivated
},
2277 { kEventClassWindow
, kEventWindowDeactivated
},
2278 { kEventClassWindow
, kEventWindowClose
},
2279 { kEventClassWindow
, kEventWindowCollapsed
},
2280 { kEventClassWindow
, kEventWindowExpanded
},
2281 { kEventClassWindow
, kEventWindowBoundsChanging
},
2282 { kEventClassWindow
, kEventWindowBoundsChanged
} };
2283 ret
= InstallWindowEventHandler( x
->xid
, windowHandler
, 10, windowEvents
, w
, 0L );
2284 ret
= InstallTrackingHandler( dndTrackingHandler
, x
->xid
, w
);
2285 ret
= InstallReceiveHandler( dndReceiveHandler
, x
->xid
, w
);
2288 if ( ! Fl_X::first
->next
) // if this is the first window, we need to bring the application to the front
2290 ProcessSerialNumber psn
;
2291 OSErr err
= GetCurrentProcess( &psn
);
2292 if ( err
==noErr
) SetFrontProcess( &psn
);
2295 if (w
->size_range_set
) w
->size_range_();
2297 if (winclass
!= kHelpWindowClass
) {
2298 Fl_Tooltip::enter(0);
2300 if (w
->size_range_set
) w
->size_range_();
2302 if (fl_show_iconic
) {
2304 CollapseWindow( x
->xid
, true ); // \todo Mac ; untested
2310 GetWindowBounds(x
->xid
, kWindowContentRgn
, &rect
);
2311 w
->x(rect
.left
); w
->y(rect
.top
);
2312 w
->w(rect
.right
-rect
.left
); w
->h(rect
.bottom
-rect
.top
);
2314 int old_event
= Fl::e_number
;
2315 w
->handle(Fl::e_number
= FL_SHOW
);
2316 Fl::e_number
= old_event
;
2317 w
->redraw(); // force draw to happen
2319 if (w
->modal()) { Fl::modal_
= w
; fl_fix_focus(); }
2325 * Tell the OS what window sizes we want to allow
2327 void Fl_Window::size_range_() {
2329 HISize minSize
= { minw
, minh
};
2330 HISize maxSize
= { maxw
?maxw
:32000, maxh
?maxh
:32000 };
2332 SetWindowResizeLimits(i
->xid
, &minSize
, &maxSize
);
2337 * returns pointer to the filename, or null if name ends with ':'
2339 const char *fl_filename_name( const char *name
)
2342 if (!name
) return (0);
2343 for ( p
= q
= name
; *p
; )
2345 if ( ( p
[0] == ':' ) && ( p
[1] == ':' ) )
2350 else if (p
[0] == '/')
2359 * set the window title bar
2360 * \todo make the titlebar icon work!
2362 void Fl_Window::label(const char *name
,const char */
*iname*/
) {
2363 Fl_Widget::label(name
);
2366 q_set_window_title(fl_xid(this), name
);
2372 * make a window visible
2374 void Fl_Window::show() {
2375 image(Fl::scheme_bg_
);
2376 if (Fl::scheme_bg_
) {
2377 labeltype(FL_NORMAL_LABEL
);
2378 align(FL_ALIGN_CENTER
| FL_ALIGN_INSIDE
| FL_ALIGN_CLIP
);
2380 labeltype(FL_NO_LABEL
);
2382 Fl_Tooltip::exit(this);
2383 if (!shown() || !i
) {
2388 if ( IsWindowCollapsed( i
->xid
) ) CollapseWindow( i
->xid
, false );
2390 BringToFront(i
->xid
);
2391 SelectWindow(i
->xid
);
2401 void Fl_Window::resize(int X
,int Y
,int W
,int H
) {
2402 if (W
<=0) W
= 1; // OS X does not like zero width windows
2404 int is_a_resize
= (W
!= w() || H
!= h());
2405 // printf("Fl_Winodw::resize(X=%d, Y=%d, W=%d, H=%d), is_a_resize=%d, resize_from_system=%p, this=%p\n",
2406 // X, Y, W, H, is_a_resize, resize_from_system, this);
2407 if (X
!= x() || Y
!= y()) set_flag(FORCE_POSITION
);
2408 else if (!is_a_resize
) return;
2409 if ( (resize_from_system
!=this) && (!parent()) && shown()) {
2412 if (W
<minw
) minw
= W
; // user request for resize takes priority
2413 if (W
>maxw
) maxw
= W
; // over a previously set size_range
2414 if (H
<minh
) minh
= H
;
2415 if (H
>maxh
) maxh
= H
;
2416 size_range(minw
, minh
, maxw
, maxh
);
2418 size_range(W
, H
, W
, H
);
2420 Rect dim
; dim
.left
=X
; dim
.top
=Y
; dim
.right
=X
+W
; dim
.bottom
=Y
+H
;
2421 SetWindowBounds(i
->xid
, kWindowContentRgn
, &dim
);
2422 Rect all
; all
.top
=-32000; all
.bottom
=32000; all
.left
=-32000; all
.right
=32000;
2423 InvalWindowRect( i
->xid
, &all
);
2425 MoveWindow(i
->xid
, X
, Y
, 0);
2428 resize_from_system
= 0;
2430 Fl_Group::resize(X
,Y
,W
,H
);
2441 * make all drawing go into this window (called by subclass flush() impl.)
2443 void Fl_Window::make_current()
2446 Fl_X::q_release_context();
2447 if ( !fl_window_region
)
2448 fl_window_region
= NewRgn();
2452 SetPort( GetWindowPort(i
->xid
) ); // \todo check for the handling of doublebuffered windows
2455 Fl_Window
*win
= this;
2458 if ( !win
->window() )
2462 win
= (Fl_Window
*)win
->window();
2464 SetOrigin( -xp
, -yp
);
2466 SetRectRgn( fl_window_region
, 0, 0, w(), h() );
2468 // \todo for performance reasons: we don't have to create this unless the child windows moved
2469 for ( Fl_X
*cx
= i
->xidChildren
; cx
; cx
= cx
->xidNext
)
2471 Fl_Window
*cw
= cx
->w
;
2472 if (!cw
->visible_r()) continue;
2473 Fl_Region r
= NewRgn();
2474 SetRectRgn( r
, cw
->x() - xp
, cw
->y() - yp
,
2475 cw
->x() + cw
->w() - xp
, cw
->y() + cw
->h() - yp
);
2476 DiffRgn( fl_window_region
, r
, fl_window_region
);
2480 err
= QDBeginCGContext(GetWindowPort(i
->xid
), &i
->gc
);
2482 fprintf(stderr
, "Error %d in QDBeginCGContext\n", (int)err
);
2484 CGContextSaveGState(fl_gc
);
2485 Fl_X::q_fill_context();
2486 #if defined(USE_CAIRO)
2487 if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately
2490 fl_clip_region( 0 );
2491 SetPortClipRegion( GetWindowPort(i
->xid
), fl_window_region
);
2493 #if defined(USE_CAIRO)
2494 // update the cairo_t context
2495 if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this);
2499 // helper function to manage the current CGContext fl_gc
2500 extern Fl_Color fl_color_
;
2501 extern class Fl_Font_Descriptor
*fl_fontsize
;
2502 extern void fl_font(class Fl_Font_Descriptor
*);
2503 extern void fl_quartz_restore_line_style_();
2505 // FLTK has only one global graphics state. This function copies the FLTK state into the
2506 // current Quartz context
2507 void Fl_X::q_fill_context() {
2512 GetPortBounds(GetWindowPort( fl_window
), &portRect
);
2513 hgt
= portRect
.bottom
-portRect
.top
;
2515 hgt
= CGBitmapContextGetHeight(fl_gc
);
2517 CGContextTranslateCTM(fl_gc
, 0.5, hgt
-0.5f
);
2518 CGContextScaleCTM(fl_gc
, 1.0f
, -1.0f
);
2519 fl_font(fl_fontsize
);
2520 fl_color(fl_color_
);
2521 fl_quartz_restore_line_style_();
2524 // The only way to reset clipping to its original state is to pop the current graphics
2525 // state and restore the global state.
2526 void Fl_X::q_clear_clipping() {
2528 CGContextRestoreGState(fl_gc
);
2529 CGContextSaveGState(fl_gc
);
2532 // Give the Quartz context back to the system
2533 void Fl_X::q_release_context(Fl_X
*x
) {
2534 if (x
&& x
->gc
!=fl_gc
) return;
2536 CGContextRestoreGState(fl_gc
);
2538 OSStatus err
= QDEndCGContext(GetWindowPort(fl_window
), &fl_gc
);
2540 fprintf(stderr
, "Error %d in QDEndCGContext\n", (int)err
);
2543 #if defined(USE_CAIRO)
2544 if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window
*) 0); // capture gc changes automatically to update the cairo context adequately
2548 void Fl_X::q_begin_image(CGRect
&rect
, int cx
, int cy
, int w
, int h
) {
2549 CGContextSaveGState(fl_gc
);
2550 CGAffineTransform mx
= CGContextGetCTM(fl_gc
);
2552 r2
.origin
.x
-= 0.5f
;
2553 r2
.origin
.y
-= 0.5f
;
2554 CGContextClipToRect(fl_gc
, r2
);
2555 mx
.d
= -1.0; mx
.tx
= -mx
.tx
;
2556 CGContextConcatCTM(fl_gc
, mx
);
2557 rect
.origin
.x
= -(mx
.tx
+0.5f
) + rect
.origin
.x
- cx
;
2558 rect
.origin
.y
= (mx
.ty
+0.5f
) - rect
.origin
.y
- h
+ cy
;
2559 rect
.size
.width
= w
;
2560 rect
.size
.height
= h
;
2563 void Fl_X::q_end_image() {
2564 CGContextRestoreGState(fl_gc
);
2567 ////////////////////////////////////////////////////////////////
2568 // Copy & Paste fltk implementation.
2569 ////////////////////////////////////////////////////////////////
2571 // fltk 1.3 clipboard support constant definitions:
2572 const CFStringRef flavorNames
[] = {
2573 CFSTR("public.utf16-plain-text"),
2574 CFSTR("public.utf8-plain-text"),
2575 CFSTR("com.apple.traditional-mac-plain-text") };
2576 const CFStringEncoding encodings
[] = {
2577 kCFStringEncodingUTF16
,
2578 kCFStringEncodingUTF8
,
2579 kCFStringEncodingMacRoman
};
2580 const size_t handledFlavorsCount
= sizeof(encodings
)/sizeof(CFStringEncoding
);
2582 // clipboard variables definitions :
2583 Fl_Widget
*fl_selection_requestor
= 0;
2584 char *fl_selection_buffer
[2];
2585 int fl_selection_length
[2];
2586 static int fl_selection_buffer_length
[2];
2588 #ifdef USE_PASTEBOARD
2589 static PasteboardRef myPasteboard
= 0;
2590 static void allocatePasteboard() {
2592 PasteboardCreate(kPasteboardClipboard
, &myPasteboard
);
2597 #ifndef USE_PASTEBOARD
2598 static ScrapRef myScrap
= 0;
2602 * create a selection
2603 * owner: widget that created the selection
2604 * stuff: pointer to selected data
2605 * size of selected data
2607 void Fl::copy(const char *stuff
, int len
, int clipboard
) {
2608 if (!stuff
|| len
<0) return;
2609 if (len
+1 > fl_selection_buffer_length
[clipboard
]) {
2610 delete[] fl_selection_buffer
[clipboard
];
2611 fl_selection_buffer
[clipboard
] = new char[len
+100];
2612 fl_selection_buffer_length
[clipboard
] = len
+100;
2614 memcpy(fl_selection_buffer
[clipboard
], stuff
, len
);
2615 fl_selection_buffer
[clipboard
][len
] = 0; // needed for direct paste
2616 fl_selection_length
[clipboard
] = len
;
2618 #ifdef USE_PASTEBOARD
2619 // FIXME no error checking done yet!
2620 allocatePasteboard();
2621 OSStatus err
= PasteboardClear(myPasteboard
);
2622 if (err
!=noErr
) return; // clear did not work, maybe not owner of clipboard.
2623 PasteboardSynchronize(myPasteboard
);
2624 CFDataRef text
= CFDataCreate(kCFAllocatorDefault
, (UInt8
*)fl_selection_buffer
[1], len
);
2625 if (text
==NULL
) return; // there was a pb creating the object, abort.
2626 err
=PasteboardPutItemFlavor(myPasteboard
, (PasteboardItemID
)1, CFSTR("public.utf8-plain-text"), text
, 0);
2629 OSStatus err
= ClearCurrentScrap(); // whatever happens we should clear the current scrap.
2630 if(err
!=noErr
) {myScrap
=0; return;} // don't get current scrap if a prev err occured.
2631 err
= GetCurrentScrap( &myScrap
);
2632 if ( err
!= noErr
) {
2636 // Previous version changed \n to \r before sending the text, but I would
2637 // prefer to leave the local buffer alone, so a copied buffer may be
2638 // needed. Check to see if this is necessary on OS/X.
2639 PutScrapFlavor( myScrap
, kScrapFlavorTypeText
, 0,
2640 len
, fl_selection_buffer
[1] );
2645 // Call this when a "paste" operation happens:
2646 void Fl::paste(Fl_Widget
&receiver
, int clipboard
) {
2648 // see if we own the selection, if not go get it:
2649 fl_selection_length
[1] = 0;
2650 #ifdef USE_PASTEBOARD
2651 OSStatus err
= noErr
;
2652 Boolean found
= false;
2653 CFDataRef flavorData
= NULL
;
2654 CFStringEncoding encoding
= 0;
2656 allocatePasteboard();
2657 PasteboardSynchronize(myPasteboard
);
2658 ItemCount nFlavor
= 0, i
, j
;
2659 err
= PasteboardGetItemCount(myPasteboard
, &nFlavor
);
2661 for (i
=1; i
<=nFlavor
; i
++) {
2662 PasteboardItemID itemID
= 0;
2663 CFArrayRef flavorTypeArray
= NULL
;
2665 err
= PasteboardGetItemIdentifier(myPasteboard
, i
, &itemID
);
2666 if (err
!=noErr
) continue;
2667 err
= PasteboardCopyItemFlavors(myPasteboard
, itemID
, &flavorTypeArray
);
2669 if (flavorTypeArray
) {CFRelease(flavorTypeArray
); flavorTypeArray
= NULL
;}
2672 CFIndex flavorCount
= CFArrayGetCount(flavorTypeArray
);
2673 for (j
= 0; j
< handledFlavorsCount
; j
++) {
2674 for (CFIndex flavorIndex
=0; flavorIndex
<flavorCount
; flavorIndex
++) {
2675 CFStringRef flavorType
= (CFStringRef
)CFArrayGetValueAtIndex(flavorTypeArray
, flavorIndex
);
2676 if (UTTypeConformsTo(flavorType
, flavorNames
[j
])) {
2677 err
= PasteboardCopyItemFlavorData( myPasteboard
, itemID
, flavorNames
[j
], &flavorData
);
2678 if(err
!= noErr
) continue;
2679 encoding
= encodings
[j
];
2686 if (flavorTypeArray
) {CFRelease(flavorTypeArray
); flavorTypeArray
= NULL
;}
2690 CFIndex len
= CFDataGetLength(flavorData
);
2691 CFStringRef mycfs
= CFStringCreateWithBytes(NULL
, CFDataGetBytePtr(flavorData
), len
, encoding
, false);
2692 CFRelease(flavorData
);
2693 len
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(mycfs
), kCFStringEncodingUTF8
) + 1;
2694 if ( len
>= fl_selection_buffer_length
[1] ) {
2695 fl_selection_buffer_length
[1] = len
;
2696 delete[] fl_selection_buffer
[1];
2697 fl_selection_buffer
[1] = new char[len
];
2699 CFStringGetCString(mycfs
, fl_selection_buffer
[1], len
, kCFStringEncodingUTF8
);
2701 len
= strlen(fl_selection_buffer
[1]);
2702 fl_selection_length
[1] = len
;
2703 convert_crlf(fl_selection_buffer
[1],len
); // turn all \r characters into \n:
2708 if (GetCurrentScrap(&scrap
) == noErr
&& scrap
!= myScrap
&&
2709 GetScrapFlavorSize(scrap
, kScrapFlavorTypeText
, &len
) == noErr
) {
2710 if ( len
>= fl_selection_buffer_length
[1] ) {
2711 fl_selection_buffer_length
[1] = len
+ 32;
2712 delete[] fl_selection_buffer
[1];
2713 fl_selection_buffer
[1] = new char[len
+ 32];
2715 fl_selection_length
[1] = len
; len
++;
2716 GetScrapFlavorData( scrap
, kScrapFlavorTypeText
, &len
,
2717 fl_selection_buffer
[1] );
2718 fl_selection_buffer
[1][fl_selection_length
[1]] = 0;
2719 convert_crlf(fl_selection_buffer
[1],len
);
2723 Fl::e_text
= fl_selection_buffer
[clipboard
];
2724 Fl::e_length
= fl_selection_length
[clipboard
];
2725 if (!Fl::e_text
) Fl::e_text
= (char *)"";
2726 receiver
.handle(FL_PASTE
);
2729 void Fl::add_timeout(double time
, Fl_Timeout_Handler cb
, void* data
)
2731 // check, if this timer slot exists already
2732 for (int i
= 0; i
< mac_timer_used
; ++i
) {
2733 MacTimeout
& t
= mac_timers
[i
];
2734 // if so, simply change the fire interval
2735 if (t
.callback
== cb
&& t
.data
== data
) {
2736 SetEventLoopTimerNextFireTime(t
.timer
, (EventTimerInterval
)time
);
2741 // no existing timer to use. Create a new one:
2743 // find an empty slot in the timer array
2744 for (int i
= 0; i
< mac_timer_used
; ++i
) {
2745 if ( !mac_timers
[i
].timer
) {
2750 // if there was no empty slot, append a new timer
2751 if (timer_id
== -1) {
2752 // make space if needed
2753 if (mac_timer_used
== mac_timer_alloc
) {
2756 timer_id
= mac_timer_used
++;
2758 // now install a brand new timer
2759 MacTimeout
& t
= mac_timers
[timer_id
];
2760 EventTimerInterval fireDelay
= (EventTimerInterval
)time
;
2761 EventLoopTimerUPP timerUPP
= NewEventLoopTimerUPP(do_timer
);
2762 EventLoopTimerRef timerRef
= 0;
2763 OSStatus err
= InstallEventLoopTimer(GetMainEventLoop(), fireDelay
, 0, timerUPP
, data
, &timerRef
);
2772 RemoveEventLoopTimer(timerRef
);
2774 DisposeEventLoopTimerUPP(timerUPP
);
2778 void Fl::repeat_timeout(double time
, Fl_Timeout_Handler cb
, void* data
)
2780 // currently, repeat_timeout does not subtract the trigger time of the previous timer event as it should.
2781 add_timeout(time
, cb
, data
);
2784 int Fl::has_timeout(Fl_Timeout_Handler cb
, void* data
)
2786 for (int i
= 0; i
< mac_timer_used
; ++i
) {
2787 MacTimeout
& t
= mac_timers
[i
];
2788 if (t
.callback
== cb
&& t
.data
== data
&& t
.pending
) {
2795 void Fl::remove_timeout(Fl_Timeout_Handler cb
, void* data
)
2797 for (int i
= 0; i
< mac_timer_used
; ++i
) {
2798 MacTimeout
& t
= mac_timers
[i
];
2799 if (t
.callback
== cb
&& ( t
.data
== data
|| data
== NULL
)) {
2805 int MacUnlinkWindow(Fl_X
*ip
, Fl_X
*start
) {
2810 if (pc
->xidNext
== ip
) {
2811 pc
->xidNext
= ip
->xidNext
;
2814 if (pc
->xidChildren
) {
2815 if (pc
->xidChildren
== ip
) {
2816 pc
->xidChildren
= ip
->xidNext
;
2819 if (MacUnlinkWindow(ip
, pc
->xidChildren
))
2825 for ( Fl_X
*pc
= Fl_X::first
; pc
; pc
= pc
->next
) {
2826 if (MacUnlinkWindow(ip
, pc
))
2833 static void MacRelinkWindow(Fl_X
*x
, Fl_X
*p
) {
2834 if (!x
|| !p
) return;
2835 // first, check if 'x' is already registered as a child of 'p'
2836 for (Fl_X
*i
= p
->xidChildren
; i
; i
=i
->xidNext
) {
2839 // now add 'x' as the first child of 'p'
2840 x
->xidNext
= p
->xidChildren
;
2844 void MacDestroyWindow(Fl_Window
*w
, WindowPtr p
) {
2845 MacUnmapWindow(w
, p
);
2846 if (w
&& !w
->parent() && p
)
2850 void MacMapWindow(Fl_Window
*w
, WindowPtr p
) {
2853 //+ link to window list
2854 if (w
&& w
->parent()) {
2855 MacRelinkWindow(Fl_X::i(w
), Fl_X::i(w
->window()));
2860 void MacUnmapWindow(Fl_Window
*w
, WindowPtr p
) {
2861 if (w
&& !w
->parent() && p
)
2863 if (w
&& Fl_X::i(w
))
2864 MacUnlinkWindow(Fl_X::i(w
));
2866 #endif // FL_DOXYGEN
2869 // End of "$Id: Fl_mac.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $".