diff options
| author | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
|---|---|---|
| committer | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
| commit | db214d1145e46d527a46d1fc2519548d2c4d23f1 (patch) | |
| tree | cf0fd9922e4d54f6beb63888f9b28c8e2a787bdf /src/Fl_cocoa.mm | |
| parent | 75fc94d6c71fe686f6dde5b41ad91cba2f6bdd6f (diff) | |
wip: fork
Diffstat (limited to 'src/Fl_cocoa.mm')
| -rw-r--r-- | src/Fl_cocoa.mm | 4793 |
1 files changed, 0 insertions, 4793 deletions
diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm deleted file mode 100644 index 02e66ed43..000000000 --- a/src/Fl_cocoa.mm +++ /dev/null @@ -1,4793 +0,0 @@ -// -// macOS-Cocoa specific code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2026 by Bill Spitzak and others. -// -// This library is free software. Distribution and use rights are outlined in -// the file "COPYING" which should have been included with this file. If this -// file is missing or damaged, see the license at: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -extern "C" { -#include <pthread.h> -} - -#include <config.h> -#include <FL/Fl.H> -#include <FL/platform.H> -#include "Fl_Window_Driver.H" -#include "Fl_Screen_Driver.H" -#include "Fl_Timeout.h" -#include <FL/Fl_Window.H> -#include <FL/Fl_Tooltip.H> -#include <FL/Fl_Image_Surface.H> -#include <FL/fl_draw.H> -#include <FL/Fl_Rect.H> -#include <FL/fl_string_functions.h> -#include "drivers/Quartz/Fl_Quartz_Graphics_Driver.H" -#include "drivers/Quartz/Fl_Quartz_Copy_Surface_Driver.H" -#include "drivers/Cocoa/Fl_Cocoa_Screen_Driver.H" -#include "drivers/Cocoa/Fl_Cocoa_Window_Driver.H" -#include "drivers/Darwin/Fl_Darwin_System_Driver.H" -#include "drivers/Cocoa/Fl_MacOS_Sys_Menu_Bar_Driver.H" -#include "print_button.h" -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <stdarg.h> -#include <math.h> -#include <limits.h> -#include <dlfcn.h> -#include <string.h> -#include <pwd.h> - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 -# error macOS SDK and deployment target version 10.7 or higher is required. -// Note: change also the warning in Fl_Darwin_System_Driver::calc_mac_os_version() below -#endif - -#import <Cocoa/Cocoa.h> -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 -# import <ScreenCaptureKit/ScreenCaptureKit.h> -#endif - -// #define DEBUG_SELECT // UNCOMMENT FOR SELECT()/THREAD DEBUGGING -#ifdef DEBUG_SELECT -#include <stdio.h> // testing -#define DEBUGMSG(msg) if ( msg ) fprintf(stderr, msg); -#define DEBUGPERRORMSG(msg) if ( msg ) perror(msg) -#define DEBUGTEXT(txt) txt -#else -#define DEBUGMSG(msg) -#define DEBUGPERRORMSG(msg) -#define DEBUGTEXT(txt) NULL -#endif /*DEBUG_SELECT*/ - -// external functions -extern void fl_fix_focus(); -extern int fl_send_system_handlers(void *e); - -// forward definition of functions in this file -// converting cr lf converter function -static void createAppleMenu(void); -static void cocoaMouseHandler(NSEvent *theEvent); -#if defined(FLTK_HAVE_PEN_SUPPORT) -static bool cocoaTabletHandler(NSEvent *theEvent, bool lock); -extern bool fl_cocoa_tablet_handler(NSEvent*, Fl_Window*); -#endif -static void clipboard_check(void); -static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h); -static NSBitmapImageRep* rect_to_NSBitmapImageRep_subwins(Fl_Window *win, int x, int y, int w, int h, bool capture_subwins); -static void drain_dropped_files_list(void); -static NSPoint FLTKtoCocoa(Fl_Window *win, int x, int y, int H); -static int get_window_frame_sizes(Fl_Window *win, int *pbx = NULL, int *pby = NULL); - -int fl_mac_os_version = Fl_Darwin_System_Driver::calc_mac_os_version(); // the version number of the running Mac OS X (e.g., 100604 for 10.6.4) - -// public variables -void *fl_capture = 0; // (NSWindow*) we need this to compensate for a missing(?) mouse capture -FLWindow *fl_window; - -// forward declarations of variables in this file -static int main_screen_height; // height of menubar-containing screen used to convert between Cocoa and FLTK global screen coordinates -// through_drawRect = YES means the drawRect: message was sent to the view, -// thus the graphics context was prepared by the system -static BOOL through_drawRect = NO; -// through_Fl_X_flush = YES means Fl_Cocoa_Window_Driver::flush() was called -static BOOL through_Fl_X_flush = NO; -static BOOL views_use_CA = NO; // YES means views are layer-backed, as on macOS 10.14 when linked with SDK 10.14 -static int im_enabled = -1; - -// OS version-dependent pasteboard type names. -// the next 5 deprecation/availability warnings can be legitimately ignored -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#pragma clang diagnostic ignored "-Wunguarded-availability" -static NSString *TIFF_pasteboard_type = NSPasteboardTypeTIFF; -static NSString *PDF_pasteboard_type = NSPasteboardTypePDF; -static NSString *PICT_pasteboard_type = @"com.apple.pict"; -static NSString *UTF8_pasteboard_type = NSPasteboardTypeString; -static NSString *fl_filenames_pboard_type = -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - (fl_mac_os_version >= 101300 ? NSPasteboardTypeFileURL : NSFilenamesPboardType); -#else - NSFilenamesPboardType; -#endif -#pragma clang diagnostic pop - -static bool in_nsapp_run = false; // true during execution of [NSApp run] -static NSMutableArray *dropped_files_list = nil; // list of files dropped at app launch -typedef void (*open_cb_f_type)(const char *); -static Fl_Window *starting_moved_window = NULL; // the moved window which brings its subwins with it - -enum { FLTKTimerEvent = 1, FLTKDataReadyEvent }; - -// Carbon functions and definitions - -typedef void *TSMDocumentID; - -extern "C" enum { - kTSMDocumentEnabledInputSourcesPropertyTag = 'enis' // from Carbon/TextServices.h -}; - -// Undocumented voodoo. Taken from Mozilla. -static const int smEnableRomanKybdsOnly = -23; - -typedef TSMDocumentID (*TSMGetActiveDocument_type)(void); -static TSMGetActiveDocument_type TSMGetActiveDocument; -typedef OSStatus (*TSMSetDocumentProperty_type)(TSMDocumentID, OSType, UInt32, void*); -static TSMSetDocumentProperty_type TSMSetDocumentProperty; -typedef OSStatus (*TSMRemoveDocumentProperty_type)(TSMDocumentID, OSType); -static TSMRemoveDocumentProperty_type TSMRemoveDocumentProperty; -typedef CFArrayRef (*TISCreateInputSourceList_type)(CFDictionaryRef, Boolean); -static TISCreateInputSourceList_type TISCreateInputSourceList; -static CFStringRef kTISTypeKeyboardLayout; -static CFStringRef kTISPropertyInputSourceType; - -typedef void (*KeyScript_type)(short); -static KeyScript_type KeyScript; - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 -const NSInteger NSControlStateValueOn = NSOnState; -const NSInteger NSControlStateValueOff = NSOffState; -#endif - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -const NSUInteger NSEventModifierFlagCommand = NSCommandKeyMask; -const NSUInteger NSEventModifierFlagOption = NSAlternateKeyMask; -const NSUInteger NSEventModifierFlagControl = NSControlKeyMask; -const NSUInteger NSEventModifierFlagShift = NSShiftKeyMask; -const NSUInteger NSEventModifierFlagCapsLock = NSAlphaShiftKeyMask; - -const NSEventType NSEventTypeLeftMouseDown = NSLeftMouseDown; -const NSEventType NSEventTypeRightMouseDown = NSRightMouseDown; -const NSEventType NSEventTypeOtherMouseDown = NSOtherMouseDown; -const NSEventType NSEventTypeLeftMouseUp = NSLeftMouseUp; -const NSEventType NSEventTypeRightMouseUp = NSRightMouseUp; -const NSEventType NSEventTypeOtherMouseUp = NSOtherMouseUp; -const NSEventType NSEventTypeLeftMouseDragged = NSLeftMouseDragged; -const NSEventType NSEventTypeRightMouseDragged = NSRightMouseDragged; -const NSEventType NSEventTypeOtherMouseDragged = NSOtherMouseDragged; -const NSEventType NSEventTypeMouseMoved = NSMouseMoved; -const NSEventType NSEventTypeMouseEntered = NSMouseEntered; -const NSEventType NSEventTypeMouseExited = NSMouseExited; -const NSEventType NSEventTypeKeyUp = NSKeyUp; -const NSEventType NSEventTypeApplicationDefined = NSApplicationDefined; - -const NSUInteger NSWindowStyleMaskResizable = NSResizableWindowMask; -const NSUInteger NSWindowStyleMaskBorderless = NSBorderlessWindowMask; -const NSUInteger NSWindowStyleMaskMiniaturizable = NSMiniaturizableWindowMask; -const NSUInteger NSWindowStyleMaskClosable = NSClosableWindowMask; -const NSUInteger NSWindowStyleMaskTitled = NSTitledWindowMask; -const NSUInteger NSWindowStyleMaskFullScreen = NSFullScreenWindowMask; - -const NSUInteger NSEventMaskAny = NSAnyEventMask; -const NSUInteger NSEventMaskSystemDefined = NSSystemDefinedMask; - -const NSUInteger NSBitmapFormatAlphaFirst = NSAlphaFirstBitmapFormat; -const NSUInteger NSBitmapFormatAlphaNonpremultiplied = NSAlphaNonpremultipliedBitmapFormat; -const short NSEventSubtypeTabletProximity = NSTabletProximityEventSubtype; -const short NSEventSubtypeTabletPoint = NSTabletPointEventSubtype; -const NSUInteger NSEventModifierFlagFunction = NSFunctionKeyMask; -#endif - -/* - * Mac keyboard lookup table - */ -static unsigned short* macKeyLookUp = NULL; - -/* - * convert the current mouse chord into the FLTK modifier state - */ -static unsigned int mods_to_e_state( NSUInteger mods ) -{ - unsigned int state = 0; - if ( mods & NSEventModifierFlagCommand ) state |= FL_META; - if ( mods & NSEventModifierFlagOption ) state |= FL_ALT; - if ( mods & NSEventModifierFlagControl ) state |= FL_CTRL; - if ( mods & NSEventModifierFlagShift ) state |= FL_SHIFT; - if ( mods & NSEventModifierFlagCapsLock ) state |= FL_CAPS_LOCK; - unsigned int ret = ( Fl::e_state & 0xff000000 ) | state; - Fl::e_state = ret; - //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods ); - return ret; -} - -// these pointers are set by the Fl::lock() function: -static void nothing() {} -void (*fl_lock_function)() = nothing; -void (*fl_unlock_function)() = nothing; - -// -// Select interface -- how it's implemented: -// When the user app configures one or more file descriptors to monitor -// with Fl::add_fd(), we start a separate thread to select() the data, -// sending a custom OSX 'FLTK data ready event' to the parent thread's -// RunApplicationLoop(), so that it triggers the data ready callbacks -// in the parent thread. -erco 04/04/04 -// -#define POLLIN 1 -#define POLLOUT 4 -#define POLLERR 8 - -// Class to handle select() 'data ready' -class DataReady -{ - struct FD - { - int fd; - short events; - void (*cb)(int, void*); - void* arg; - }; - int nfds, fd_array_size; - FD *fds; - pthread_t tid; // select()'s thread id - - // Data that needs to be locked (all start with '_') - pthread_mutex_t _datalock; // data lock - fd_set _fdsets[3]; // r/w/x sets user wants to monitor - int _maxfd; // max fd count to monitor - int _cancelpipe[2]; // pipe used to help cancel thread - -public: - DataReady() - { - nfds = 0; - fd_array_size = 0; - fds = 0; - tid = 0; - - pthread_mutex_init(&_datalock, NULL); - FD_ZERO(&_fdsets[0]); FD_ZERO(&_fdsets[1]); FD_ZERO(&_fdsets[2]); - _cancelpipe[0] = _cancelpipe[1] = 0; - _maxfd = -1; - } - - ~DataReady() - { - CancelThread(DEBUGTEXT("DESTRUCTOR\n")); - if (fds) { free(fds); fds = 0; } - nfds = 0; - } - - // Locks - // The convention for locks: volatile vars start with '_', - // and must be locked before use. Locked code is prefixed - // with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco - // - void DataLock() { pthread_mutex_lock(&_datalock); } - void DataUnlock() { pthread_mutex_unlock(&_datalock); } - - // Accessors - int IsThreadRunning() { return(tid ? 1 : 0); } - int GetNfds() { return(nfds); } - int GetCancelPipe(int ix) { return(_cancelpipe[ix]); } - fd_set GetFdset(int ix) { return(_fdsets[ix]); } - - // Methods - void AddFD(int n, int events, void (*cb)(int, void*), void *v); - void RemoveFD(int n, int events); - int CheckData(fd_set& r, fd_set& w, fd_set& x); - void HandleData(fd_set& r, fd_set& w, fd_set& x); - static void* DataReadyThread(void *self); - void StartThread(void); - void CancelThread(const char *reason); -}; - -static DataReady dataready; - -void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v) -{ - RemoveFD(n, events); - int i = nfds++; - if (i >= fd_array_size) - { - fl_open_display(); // necessary for NSApp to be defined and the event loop to work - FD *temp; - fd_array_size = 2*fd_array_size+1; - if (!fds) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); } - else { temp = (FD*)realloc(fds, fd_array_size*sizeof(FD)); } - if (!temp) return; - fds = temp; - } - fds[i].cb = cb; - fds[i].arg = v; - fds[i].fd = n; - fds[i].events = events; - DataLock(); - /*LOCK*/ if (events & POLLIN) FD_SET(n, &_fdsets[0]); - /*LOCK*/ if (events & POLLOUT) FD_SET(n, &_fdsets[1]); - /*LOCK*/ if (events & POLLERR) FD_SET(n, &_fdsets[2]); - /*LOCK*/ if (n > _maxfd) _maxfd = n; - DataUnlock(); -} - -// Remove an FD from the array -void DataReady::RemoveFD(int n, int events) -{ - int i,j; - _maxfd = -1; // recalculate maxfd on the fly - for (i=j=0; i<nfds; i++) { - if (fds[i].fd == n) { - int e = fds[i].events & ~events; - if (!e) continue; // if no events left, delete this fd - fds[i].events = e; - } - if (fds[i].fd > _maxfd) _maxfd = fds[i].fd; - // move it down in the array if necessary: - if (j<i) { - fds[j] = fds[i]; - } - j++; - } - nfds = j; - DataLock(); - /*LOCK*/ if (events & POLLIN) FD_CLR(n, &_fdsets[0]); - /*LOCK*/ if (events & POLLOUT) FD_CLR(n, &_fdsets[1]); - /*LOCK*/ if (events & POLLERR) FD_CLR(n, &_fdsets[2]); - DataUnlock(); -} - -// CHECK IF USER DATA READY, RETURNS r/w/x INDICATING WHICH IF ANY -int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x) -{ - int ret; - DataLock(); - /*LOCK*/ timeval t = { 0, 1 }; // quick check - /*LOCK*/ r = _fdsets[0], w = _fdsets[1], x = _fdsets[2]; - /*LOCK*/ ret = ::select(_maxfd+1, &r, &w, &x, &t); - DataUnlock(); - if ( ret == -1 ) { - DEBUGPERRORMSG("CheckData(): select()"); - } - return(ret); -} - -// HANDLE DATA READY CALLBACKS -void DataReady::HandleData(fd_set& r, fd_set& w, fd_set& x) -{ - for (int i=0; i<nfds; i++) { - int f = fds[i].fd; - short revents = 0; - if (FD_ISSET(f, &r)) revents |= POLLIN; - if (FD_ISSET(f, &w)) revents |= POLLOUT; - if (FD_ISSET(f, &x)) revents |= POLLERR; - if (fds[i].events & revents) { - DEBUGMSG("DOING CALLBACK: "); - fds[i].cb(f, fds[i].arg); - DEBUGMSG("DONE\n"); - } - } -} - -// DATA READY THREAD -// This thread watches for changes in user's file descriptors. -// Sends a 'data ready event' to the main thread if any change. -// -void* DataReady::DataReadyThread(void *o) -{ - DataReady *self = (DataReady*)o; - while ( 1 ) { // loop until thread cancel or error - // Thread safe local copies of data before each select() - self->DataLock(); - /*LOCK*/ int maxfd = self->_maxfd; - /*LOCK*/ fd_set r = self->GetFdset(0); - /*LOCK*/ fd_set w = self->GetFdset(1); - /*LOCK*/ fd_set x = self->GetFdset(2); - /*LOCK*/ int cancelpipe = self->GetCancelPipe(0); - /*LOCK*/ if ( cancelpipe > maxfd ) maxfd = cancelpipe; - /*LOCK*/ FD_SET(cancelpipe, &r); // add cancelpipe to fd's to watch - /*LOCK*/ FD_SET(cancelpipe, &x); - self->DataUnlock(); - // timeval t = { 1000, 0 }; // 1000 seconds; - timeval t = { 2, 0 }; // HACK: 2 secs prevents 'hanging' problem - int ret = ::select(maxfd+1, &r, &w, &x, &t); - pthread_testcancel(); // OSX 10.0.4 and older: needed for parent to cancel - switch ( ret ) { - case 0: // NO DATA - continue; - case -1: // ERROR - { - DEBUGPERRORMSG("CHILD THREAD: select() failed"); - return(NULL); // error? exit thread - } - default: // DATA READY - { - if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x)) // cancel? - { return(NULL); } // just exit - DEBUGMSG("CHILD THREAD: DATA IS READY\n"); - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:0 context:NULL subtype:FLTKDataReadyEvent data1:0 data2:0]; - [NSApp postEvent:event atStart:NO]; - [localPool release]; - return(NULL); // done with thread - } - } - } -} - -// START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE -void DataReady::StartThread(void) -{ - CancelThread(DEBUGTEXT("STARTING NEW THREAD\n")); - DataLock(); - /*LOCK*/ pipe(_cancelpipe); // pipe for sending cancel msg to thread - DataUnlock(); - DEBUGMSG("*** START THREAD\n"); - pthread_create(&tid, NULL, DataReadyThread, (void*)this); -} - -// CANCEL 'DATA READY' THREAD, CLOSE PIPE -void DataReady::CancelThread(const char *reason) -{ - if ( tid ) { - DEBUGMSG("*** CANCEL THREAD: "); - DEBUGMSG(reason); - if ( pthread_cancel(tid) == 0 ) { // cancel first - DataLock(); - /*LOCK*/ write(_cancelpipe[1], "x", 1); // wake thread from select - DataUnlock(); - pthread_join(tid, NULL); // wait for thread to finish - } - tid = 0; - DEBUGMSG("(JOINED) OK\n"); - } - // Close pipe if open - DataLock(); - /*LOCK*/ if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; } - /*LOCK*/ if ( _cancelpipe[1] ) { close(_cancelpipe[1]); _cancelpipe[1] = 0; } - DataUnlock(); -} - -void Fl_Darwin_System_Driver::add_fd( int n, int events, void (*cb)(int, void*), void *v ) -{ - dataready.AddFD(n, events, cb, v); -} - -void Fl_Darwin_System_Driver::add_fd(int fd, void (*cb)(int, void*), void* v) -{ - dataready.AddFD(fd, POLLIN, cb, v); -} - -void Fl_Darwin_System_Driver::remove_fd(int n, int events) -{ - dataready.RemoveFD(n, events); -} - -void Fl_Darwin_System_Driver::remove_fd(int n) -{ - dataready.RemoveFD(n, -1); -} - -/* - * Check if there is actually a message pending - */ -int Fl_Darwin_System_Driver::ready() - -{ - NSEvent *retval = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate dateWithTimeIntervalSinceNow:0] - inMode:NSDefaultRunLoopMode - dequeue:NO]; - return retval != nil; -} - - -static void processFLTKEvent(void) { - fl_lock_function(); - dataready.CancelThread(DEBUGTEXT("DATA READY EVENT\n")); - - // CHILD THREAD TELLS US DATA READY - // Check to see what's ready, and invoke user's cb's - // - fd_set r,w,x; - switch(dataready.CheckData(r,w,x)) { - case 0: // NO DATA - break; - case -1: // ERROR - break; - default: // DATA READY - dataready.HandleData(r,w,x); - break; - } - fl_unlock_function(); - return; -} - - -/* - * break the current event loop - */ -void Fl_Cocoa_Screen_Driver::breakMacEventLoop() -{ - NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 timestamp:0 - windowNumber:0 context:NULL - subtype:FLTKTimerEvent - data1:0 - data2:0]; - [NSApp postEvent:event atStart:NO]; -} - - -@interface FLWindow : NSWindow { - Fl_Window *w; -} -- (FLWindow*)initWithFl_W:(Fl_Window *)flw - contentRect:(NSRect)rect - styleMask:(NSUInteger)windowStyle; -- (Fl_Window *)getFl_Window; -- (void)recursivelySendToSubwindows:(SEL)sel applyToSelf:(BOOL)b; -- (void)setSubwindowFrame; -- (void)checkSubwindowFrame; -- (void)waitForExpose; -- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen; -- (NSPoint)convertBaseToScreen:(NSPoint)aPoint; -- (NSBitmapImageRep*)rect_to_NSBitmapImageRep:(Fl_Rect*)r; -- (void)makeKeyWindow; -@end - - -@interface FLView : NSView <NSTextInput, NSTextInputClient, NSDraggingSource> { - BOOL in_key_event; // YES means keypress is being processed by handleEvent - BOOL need_handle; // YES means Fl::handle(FL_KEYBOARD,) is needed after handleEvent processing - NSInteger identifier; - NSRange selectedRange; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -@public - CGContextRef aux_bitmap; // all drawing to view goes there and is finally copied to the CALayer -#endif -} -+ (void)prepareEtext:(NSString*)aString; -+ (void)concatEtext:(NSString*)aString; -- (BOOL)process_keydown:(NSEvent*)theEvent; -- (id)initWithFrame:(NSRect)frameRect; -- (void)drawRect:(NSRect)rect; -- (BOOL)acceptsFirstResponder; -- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent; -- (void)resetCursorRects; -- (BOOL)performKeyEquivalent:(NSEvent*)theEvent; -- (void)mouseUp:(NSEvent *)theEvent; -- (void)rightMouseUp:(NSEvent *)theEvent; -- (void)otherMouseUp:(NSEvent *)theEvent; -- (void)mouseDown:(NSEvent *)theEvent; -- (void)rightMouseDown:(NSEvent *)theEvent; -- (void)otherMouseDown:(NSEvent *)theEvent; -- (void)mouseMoved:(NSEvent *)theEvent; -- (void)mouseEntered:(NSEvent *)theEvent; -- (void)mouseExited:(NSEvent *)theEvent; -- (void)mouseDragged:(NSEvent *)theEvent; -- (void)rightMouseDragged:(NSEvent *)theEvent; -- (void)otherMouseDragged:(NSEvent *)theEvent; -- (void)scrollWheel:(NSEvent *)theEvent; -- (void)magnifyWithEvent:(NSEvent *)theEvent; -- (void)keyDown:(NSEvent *)theEvent; -- (void)keyUp:(NSEvent *)theEvent; -- (void)flagsChanged:(NSEvent *)theEvent; -- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender; -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; -- (void)draggingExited:(id < NSDraggingInfo >)sender; -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; -- (void)updateTrackingAreas; -- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context; -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation; -- (BOOL)did_view_resolution_change; -#if defined(FLTK_HAVE_PEN_SUPPORT) -- (void)tabletProximity:(NSEvent *)theEvent; -- (void)tabletPoint:(NSEvent *)theEvent; -#endif -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -- (void)create_aux_bitmap:(CGContextRef)gc retina:(BOOL)r; -- (void)reset_aux_bitmap; -#endif -@end - - -@implementation FLWindow -- (void)close -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (views_use_CA) [(FLView*)[self contentView] reset_aux_bitmap]; -#endif - [[self standardWindowButton:NSWindowDocumentIconButton] setImage:nil]; - [super close]; - while (true) { - NSArray *a = [[self contentView] trackingAreas]; - if ([a count] == 0) break; - NSTrackingArea *ta = (NSTrackingArea*)[a objectAtIndex:0]; - [[self contentView] removeTrackingArea:ta]; - } - // when a fullscreen window is closed, windowDidResize may be sent after the close message was sent - // and before the FLWindow receives the final dealloc message - w = NULL; -} -- (NSPoint)convertBaseToScreen:(NSPoint)aPoint -{ - NSRect r = [self convertRectToScreen:NSMakeRect(aPoint.x, aPoint.y, 0, 0)]; - return r.origin; -} -- (FLWindow*)initWithFl_W:(Fl_Window *)flw - contentRect:(NSRect)rect - styleMask:(NSUInteger)windowStyle -{ - self = [super initWithContentRect:rect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO]; - if (self) { - w = flw; - [self setRestorable:NO]; - } - return self; -} -- (Fl_Window *)getFl_Window -{ - return w; -} - -- (BOOL)canBecomeKeyWindow -{ - if (Fl::modal_ && (Fl::modal_ != w)) - return NO; // prevent the caption to be redrawn as active on click - // when another modal window is currently the key win - return !(!w || w->output() || w->tooltip_window() || w->menu_window() || w->parent()); -} - -- (BOOL)canBecomeMainWindow -{ - if (Fl::modal_ && (Fl::modal_ != w)) - return NO; // prevent the caption to be redrawn as active on click - // when another modal window is currently the key win - - return !(!w || w->tooltip_window() || w->menu_window() || w->parent()); -} - -- (void)recursivelySendToSubwindows:(SEL)sel applyToSelf:(BOOL)b -{ - if (b) [self performSelector:sel]; - NSEnumerator *enumerator = [[self childWindows] objectEnumerator]; - id child; - while ((child = [enumerator nextObject]) != nil) { - if ([child isKindOfClass:[FLWindow class]]) [child recursivelySendToSubwindows:sel applyToSelf:YES]; - } -} - -- (void)setSubwindowFrame { // have the cocoa position and size of a (sub)window follow its FLTK data - Fl_Window *parent = w->window(); - if (!w->visible_r()) return; - NSPoint pt = FLTKtoCocoa(w, w->x(), w->y(), w->h()); - float s = Fl::screen_driver()->scale(0); - int bt = parent ? 0 : get_window_frame_sizes(w); - NSRect rp = NSMakeRect(round(pt.x), round(pt.y), round(s * w->w()), round(s * w->h()) + bt); - if (!NSEqualRects(rp, [self frame])) { - [self setFrame:rp display:(views_use_CA ? NO : YES)]; - } - if (parent && ![self parentWindow]) { // useful when subwin is first shown, not when moved - FLWindow *pxid = fl_xid(parent); - [pxid addChildWindow:self ordered:NSWindowAbove]; // needs OS X 10.2 - [self orderWindow:NSWindowAbove relativeTo:[pxid windowNumber]]; // necessary under 10.3 - } -} - -- (void)checkSubwindowFrame { - if (!w->parent()) return; - // make sure this subwindow doesn't leak out of its parent window - Fl_Window *from = w, *parent; - CGRect full = CGRectMake(0, 0, w->w(), w->h()); // full subwindow area - CGRect srect = full; // will become new subwindow clip - int fromx = 0, fromy = 0; - while ((parent = from->window()) != NULL) { // loop over all parent windows - fromx -= from->x(); // parent origin in subwindow's coordinates - fromy -= from->y(); - CGRect prect = CGRectMake(fromx, fromy, parent->w(), parent->h()); - srect = CGRectIntersection(prect, srect); // area of subwindow inside its parent - from = parent; - } - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(w); - CGRect *r = d->subRect(); - CGRect current_clip = (r ? *r : full); // current subwindow clip - if (!CGRectEqualToRect(srect, current_clip)) { // if new clip differs from current clip - delete r; - FLWindow *xid = fl_xid(w); - FLView *view = (FLView*)[xid contentView]; - if (CGRectEqualToRect(srect, full)) { - r = NULL; - } else { - r = new CGRect(srect); - if (r->size.width == 0 && r->size.height == 0) r->origin.x = r->origin.y = 0; - } - d->subRect(r); - w->redraw(); - if (fl_mac_os_version < 100900) { - NSInteger parent_num = [fl_xid(w->window()) windowNumber]; - [xid orderWindow:NSWindowBelow relativeTo:parent_num]; - [xid orderWindow:NSWindowAbove relativeTo:parent_num]; - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (!views_use_CA || view->aux_bitmap) -#endif - [view display]; // subwindow needs redrawn - } -} - --(void)waitForExpose -{ - if ([self getFl_Window]->shown()) { - // this makes freshly created windows appear on the screen, if they are not there already - NSModalSession session = [NSApp beginModalSessionForWindow:self]; - [NSApp runModalSession:session]; - [NSApp endModalSession:session]; - } -} - -/* With Mac OS 10.11 the green window button makes window fullscreen (covers system menu bar and dock). - When there are subwindows, they are by default constrained not to cover the menu bar - (this is arguably a Mac OS bug). - Overriding the constrainFrameRect:toScreen: method removes this constraint. - */ -- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen -{ - if ([self parentWindow]) return frameRect; // do not constrain subwindows - return [super constrainFrameRect:frameRect toScreen:screen]; // will prevent a window from going above the menu bar -} -- (NSBitmapImageRep*)rect_to_NSBitmapImageRep:(Fl_Rect*)r { - return rect_to_NSBitmapImageRep(w, r->x(), r->y(), r->w(), r->h()); -} -- (void)makeKeyWindow { - // Necessary in this scenario at least: - // transition of a subwindow-containing window from multiscreen-fullscreen mode to normal mode. - if ([self canBecomeKeyWindow]) [super makeKeyWindow]; -} -@end - -@interface FLApplication : NSObject -{ -} -+ (void)sendEvent:(NSEvent *)theEvent; -@end - -/* - * This function is the central event handler. - * It reads events from the event queue using the given maximum time - */ -static int do_queued_events( double time = 0.0 ) -{ - static int got_events; // not sure the static is necessary here - got_events = 0; - - // Check for re-entrant condition - if ( dataready.IsThreadRunning() ) { - dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); - } - - // Start thread to watch for data ready - if ( dataready.GetNfds() ) { - dataready.StartThread(); - } - - // Elapse timeouts and calculate waiting time - Fl_Timeout::elapse_timeouts(); - time = Fl_Timeout::time_to_wait(time); - - fl_unlock_function(); - NSEvent *event; - while ( (event = [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate dateWithTimeIntervalSinceNow:time] - inMode:NSDefaultRunLoopMode - dequeue:YES]) != nil ) { - got_events = 1; - [FLApplication sendEvent:event]; // will then call [NSApplication sendevent:] - time = 0; - } - fl_lock_function(); - - return got_events; -} - -double Fl_Darwin_System_Driver::wait(double time_to_wait) -{ - if (dropped_files_list) { // when the list of dropped files is not empty, open one and remove it from list - drain_dropped_files_list(); - } - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - time_to_wait = Fl_System_Driver::wait(time_to_wait); - // the deprecation warnings can be ignored because they run only for macOS < 10.11 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if (fl_mac_os_version < 101100) NSDisableScreenUpdates(); // deprecated 10.11 - Fl::flush(); - if (fl_mac_os_version < 101100) NSEnableScreenUpdates(); // deprecated 10.11 -#pragma clang diagnostic pop - if (Fl::idle()) // 'idle' may have been set within flush() - time_to_wait = 0.0; - int retval = do_queued_events(time_to_wait); - - Fl_Cocoa_Window_Driver::q_release_context(); - [pool release]; - return retval; -} - -static NSInteger max_normal_window_level(void) -{ - Fl_X *x; - NSInteger max_level; - - max_level = 0; - - for (x = Fl_X::first;x;x = x->next) { - NSInteger level; - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (!win || !cw || ![cw isVisible]) - continue; - if (win->modal() || win->non_modal()) - continue; - level = [cw level]; - if (level >= max_level) - max_level = level; - } - - return max_level; -} - -// appropriate window level for modal windows -static NSInteger modal_window_level(void) -{ - NSInteger level; - - level = max_normal_window_level(); - if (level < NSStatusWindowLevel) - return NSStatusWindowLevel; - - // Need some room for non-modal windows - level += 2; - - // We cannot exceed this - if (level > CGShieldingWindowLevel()) - return CGShieldingWindowLevel(); - - return level; -} - -// appropriate window level for non-modal windows -static NSInteger non_modal_window_level(void) -{ - NSInteger level; - - level = max_normal_window_level(); - if (level < NSFloatingWindowLevel) - return NSFloatingWindowLevel; - - level += 1; - - if (level > CGShieldingWindowLevel()) - return CGShieldingWindowLevel(); - - return level; -} - -// makes sure modal and non-modal windows stay on top -static void fixup_window_levels(void) -{ - NSInteger modal_level, non_modal_level; - - Fl_X *x; - FLWindow *prev_modal, *prev_non_modal; - - modal_level = modal_window_level(); - non_modal_level = non_modal_window_level(); - - prev_modal = NULL; - prev_non_modal = NULL; - - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (!win || !cw || ![cw isVisible]) - continue; - if (win->modal()) { - if ([cw level] != modal_level) { - [cw setLevel:modal_level]; - // changing level puts then in front, so make sure the - // stacking isn't messed up - if (prev_modal != NULL) - [cw orderWindow:NSWindowBelow - relativeTo:[prev_modal windowNumber]]; - } - prev_modal = cw; - } else if (win->non_modal()) { - if ([cw level] != non_modal_level) { - [cw setLevel:non_modal_level]; - if (prev_non_modal != NULL) - [cw orderWindow:NSWindowBelow - relativeTo:[prev_non_modal windowNumber]]; - } - prev_non_modal = cw; - } - } -} - - -// updates Fl::e_x, Fl::e_y, Fl::e_x_root, and Fl::e_y_root -static void update_e_xy_and_e_xy_root(NSWindow *nsw) -{ - NSPoint pt; - pt = [nsw mouseLocationOutsideOfEventStream]; - float s = Fl::screen_driver()->scale(0); - Fl::e_x = int(pt.x / s); - Fl::e_y = int(([[nsw contentView] frame].size.height - pt.y)/s); - pt = [NSEvent mouseLocation]; - Fl::e_x_root = int(pt.x/s); - Fl::e_y_root = int((main_screen_height - pt.y)/s); -} - - -/* - * Cocoa Mousewheel handler - */ -static void cocoaMouseWheelHandler(NSEvent *theEvent) -{ - // Handle the new "MightyMouse" mouse wheel events. Please, someone explain - // to me why Apple changed the API on this even though the current API - // supports two wheels just fine. Matthias, - fl_lock_function(); - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - Fl::first_window(window); - // Under OSX, mousewheel deltas are floats, but fltk only supports ints. - float s = Fl::screen_driver()->scale(0); - float edx = [theEvent deltaX]; - float edy = [theEvent deltaY]; - int dx = roundf(edx / s); - int dy = roundf(edy / s); - // make sure that even small wheel movements count at least as one unit - if (edx>0.0f) dx++; else if (edx<0.0f) dx--; - if (edy>0.0f) dy++; else if (edy<0.0f) dy--; - // allow both horizontal and vertical movements to be processed by the widget - if (dx) { - Fl::e_dx = -dx; - Fl::e_dy = 0; - Fl::handle( FL_MOUSEWHEEL, window ); - } - if (dy) { - Fl::e_dx = 0; - Fl::e_dy = -dy; - Fl::handle( FL_MOUSEWHEEL, window ); - } - fl_unlock_function(); -} - -/* - * Cocoa Magnify Gesture Handler - */ -static void cocoaMagnifyHandler(NSEvent *theEvent) -{ - fl_lock_function(); - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - if ( !window->shown() ) { - fl_unlock_function(); - return; - } - Fl::first_window(window); - Fl::e_dy = [theEvent magnification]*1000; // 10.5.2 - if ( Fl::e_dy) { - NSPoint pos = [theEvent locationInWindow]; - pos.y = window->h() - pos.y; - NSUInteger mods = [theEvent modifierFlags]; - mods_to_e_state( mods ); - update_e_xy_and_e_xy_root([theEvent window]); - Fl::handle( FL_ZOOM_GESTURE, window ); - } - fl_unlock_function(); -} - -#if defined(FLTK_HAVE_PEN_SUPPORT) - -static bool cocoaTabletHandler(NSEvent *theEvent, bool lock) -{ - if (lock) fl_lock_function(); - auto theWindow = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - auto ret = fl_cocoa_tablet_handler(theEvent, theWindow); - if (lock) fl_unlock_function(); - return ret; -} - -#endif // FLTK_HAVE_PEN_SUPPORT - -namespace Fl { -namespace Private { -// Global mouse position at mouse down event -int e_x_down { 0 }; -int e_y_down { 0 }; -}; // namespace Private -}; // namespace Fl - -/* - * Cocoa Mouse Button Handler - */ -static void cocoaMouseHandler(NSEvent *theEvent) -{ - static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2, FL_Button+4, FL_Button+5 }; - - fl_lock_function(); - -#if defined(FLTK_HAVE_PEN_SUPPORT) - // Handle tablet proximity and point subevents - if ( ([theEvent type] != NSEventTypeMouseEntered) // does not have a subtype - && ([theEvent type] != NSEventTypeMouseExited) ) // does not have a subtype - { - if ( ([theEvent subtype] == NSEventSubtypeTabletPoint) - || ([theEvent subtype] == NSEventSubtypeTabletProximity) ) - { - if (cocoaTabletHandler(theEvent, false)) { - fl_unlock_function(); - return; - } - // else fall through into mouse event handling - } - } -#endif // FLTK_HAVE_PEN_SUPPORT - - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - if (!window || !window->shown() ) { - fl_unlock_function(); - return; - } - - NSPoint pos = [theEvent locationInWindow]; - float s = Fl::screen_driver()->scale(0); - pos.x /= s; pos.y /= s; - pos.y = window->h() - pos.y; - NSInteger btn = [theEvent buttonNumber] + 1; - NSUInteger mods = [theEvent modifierFlags]; - int sendEvent = 0; - - NSEventType etype = [theEvent type]; - if (etype == NSEventTypeLeftMouseDown || etype == NSEventTypeRightMouseDown || - etype == NSEventTypeOtherMouseDown) { - if (btn == 1) Fl::e_state |= FL_BUTTON1; - else if (btn == 3) Fl::e_state |= FL_BUTTON2; - else if (btn == 2) Fl::e_state |= FL_BUTTON3; - else if (btn == 4) Fl::e_state |= FL_BUTTON4; - else if (btn == 5) Fl::e_state |= FL_BUTTON5; - } - else if (etype == NSEventTypeLeftMouseUp || etype == NSEventTypeRightMouseUp || - etype == NSEventTypeOtherMouseUp) { - if (btn == 1) Fl::e_state &= ~FL_BUTTON1; - else if (btn == 3) Fl::e_state &= ~FL_BUTTON2; - else if (btn == 2) Fl::e_state &= ~FL_BUTTON3; - else if (btn == 4) Fl::e_state &= ~FL_BUTTON4; - else if (btn == 5) Fl::e_state &= ~FL_BUTTON5; - } - - switch ( etype ) { - case NSEventTypeLeftMouseDown: - case NSEventTypeRightMouseDown: - case NSEventTypeOtherMouseDown: - sendEvent = FL_PUSH; - Fl::e_is_click = 1; - Fl::Private::e_x_down = (int)pos.x; - Fl::Private::e_y_down = (int)pos.y; - if ([theEvent clickCount] > 1) - Fl::e_clicks++; - else - Fl::e_clicks = 0; - // fall through - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseUp: - case NSEventTypeOtherMouseUp: - if ( !window ) break; - if ( !sendEvent ) { - sendEvent = FL_RELEASE; - } - Fl::e_keysym = keysym[ btn ]; - // fall through - case NSEventTypeMouseMoved: - if ( !sendEvent ) { - sendEvent = FL_MOVE; - } - // fall through - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeOtherMouseDragged: { - if ( !sendEvent ) { - sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG - if ( (fabs(pos.x - Fl::Private::e_x_down) > 5) || - (fabs(pos.y - Fl::Private::e_y_down) > 5)) - Fl::e_is_click = 0; - } - mods_to_e_state( mods ); - update_e_xy_and_e_xy_root([theEvent window]); - Fl::handle( sendEvent, window ); - } - break; - case NSEventTypeMouseEntered : - if ([theEvent window]) update_e_xy_and_e_xy_root([theEvent window]); - Fl::handle(FL_ENTER, window); - break; - case NSEventTypeMouseExited : - Fl::handle(FL_LEAVE, window); - break; - default: - break; - } - - fl_unlock_function(); - - return; -} - - -@interface FLTextView : NSTextView // this subclass is only needed under OS X < 10.6 -{ - BOOL isActive; -} -+ (void)initialize; -+ (FLTextView*)singleInstance; -- (void)insertText:(id)aString; -- (void)doCommandBySelector:(SEL)aSelector; -- (void)setActive:(BOOL)a; -@end -static FLTextView *fltextview_instance = nil; -@implementation FLTextView -+ (void)initialize { - NSRect rect={{0,0},{20,20}}; - fltextview_instance = [[FLTextView alloc] initWithFrame:rect]; -} -+ (FLTextView*)singleInstance { - return fltextview_instance; -} -- (void)insertText:(id)aString -{ - if (isActive) [[[NSApp keyWindow] contentView] insertText:aString]; -} -- (void)doCommandBySelector:(SEL)aSelector -{ - [[[NSApp keyWindow] contentView] doCommandBySelector:aSelector]; -} -- (void)setActive:(BOOL)a -{ - isActive = a; -} -@end - - -@interface FLWindowDelegate : NSObject <NSWindowDelegate> -+ (void)initialize; -+ (FLWindowDelegate*)singleInstance; -- (void)windowDidMove:(NSNotification *)notif; -- (void)view_did_resize:(NSNotification *)notif; -- (void)windowDidResignKey:(NSNotification *)notif; -- (void)windowDidBecomeKey:(NSNotification *)notif; -- (void)windowDidBecomeMain:(NSNotification *)notif; -- (void)windowDidDeminiaturize:(NSNotification *)notif; -- (void)fl_windowMiniaturize:(NSNotification *)notif; -- (void)windowDidMiniaturize:(NSNotification *)notif; -- (void)windowWillEnterFullScreen:(NSNotification *)notif; -- (void)windowWillExitFullScreen:(NSNotification *)notif; -- (BOOL)windowShouldClose:(id)fl; -- (void)anyWindowWillClose:(NSNotification *)notif; -- (void)doNothing:(id)unused; -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; -@end - - -/* make subwindows re-appear after appl unhide or window deminiaturize - (not necessary with 10.5 and above) - */ -static void orderfront_subwindows(FLWindow *xid) -{ - NSArray *children = [xid childWindows]; // 10.2 - NSEnumerator *enumerator = [children objectEnumerator]; - id child; - while ((child = [enumerator nextObject]) != nil) { // this undo-redo seems necessary under 10.3 - [xid removeChildWindow:child]; - [xid addChildWindow:child ordered:NSWindowAbove]; - [child orderWindow:NSWindowAbove relativeTo:[xid windowNumber]]; - orderfront_subwindows(child); - } -} - - -// compute coordinates of the win top left in FLTK units -static void CocoatoFLTK(Fl_Window *win, int &x, int &y) { - NSPoint ori; - FLWindow *nsw = fl_xid(win); - ori = [nsw convertBaseToScreen:NSMakePoint(0, [[nsw contentView] frame].size.height)]; - float s = Fl::screen_driver()->scale(0); - x = (int)lround(ori.x / s); - y = (int)lround((main_screen_height - ori.y) / s); - while (win->parent()) {win = win->window(); x -= win->x(); y -= win->y();} -} - -// return Cocoa coordinates of the point in window win at (x,y) FLTK units -static NSPoint FLTKtoCocoa(Fl_Window *win, int x, int y, int H) { - float s = Fl::screen_driver()->scale(0); - while (win->parent()) {win = win->window(); x += win->x(); y += win->y();} - return NSMakePoint(round(x * s), main_screen_height - round((y + H)*s)); -} - -static FLWindowDelegate *flwindowdelegate_instance = nil; -@implementation FLWindowDelegate -+ (void)initialize -{ - if (self == [FLWindowDelegate self]) { - flwindowdelegate_instance = [FLWindowDelegate alloc]; - flwindowdelegate_instance = [flwindowdelegate_instance init]; - } -} -+ (FLWindowDelegate*)singleInstance { - return flwindowdelegate_instance; -} -- (void)windowDidMove:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - if (!window->parent()) starting_moved_window = window; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - FLView *view = (FLView*)[nsw contentView]; - if (views_use_CA && [view did_view_resolution_change]) { - if (window->as_gl_window() && Fl::use_high_res_GL()) [view setNeedsDisplay:YES]; // necessary with macOS ≥ 10.14.2; harmless before - } -#endif - if (window == starting_moved_window) { - // we update 'main_screen_height' here because it's wrong just after screen config changes - main_screen_height = CGDisplayBounds(CGMainDisplayID()).size.height; - int X, Y; - CocoatoFLTK(window, X, Y); - if (window->x() != X || window->y() != Y) { - if (!Fl_Cocoa_Window_Driver::driver(window)->through_resize()) - window->position(X, Y); - else - window->Fl_Widget::resize(X,Y,window->w(),window->h()); - } - update_e_xy_and_e_xy_root(nsw); - // at least since MacOS 10.9: OS moves subwindows contained in a moved window - // setSubwindowFrame is no longer necessary. - if (fl_mac_os_version < 100900) [nsw recursivelySendToSubwindows:@selector(setSubwindowFrame) applyToSelf:NO]; - if (window->parent()) [nsw recursivelySendToSubwindows:@selector(checkSubwindowFrame) applyToSelf:YES]; - starting_moved_window = NULL; - } - if (!window->parent()) { - int nscreen = Fl::screen_num(window->x(), window->y(), window->w(), window->h()); - Fl_Window_Driver::driver(window)->screen_num(nscreen); - } - fl_unlock_function(); -} - -/* - This method is called whenever the view of an Fl_Window changes size. - - This can happen for various reasons: - - - the user resizes a desktop window (NSViewFrameDidChangeNotification) - Fl_Cocoa_Window_Driver::driver(window)->through_resize() == 0 for the top level window - Fl_Window::is_a_rescale() == 0 - - the app scale is changed (the Cocoa size changes, but the FLTK size remains) - Fl_Cocoa_Window_Driver::driver(window)->through_resize() == 1 - Fl_Window::is_a_rescale() == 1 - - a window is resized by application code: Fl_Window:resize() - Fl_Cocoa_Window_Driver::driver(window)->through_resize() == 1 - Fl_Window::is_a_rescale() == 0 - - Note that a top level window must be treated differently than a subwindow - (an Fl_Window that is the child of another window). - - Also note, it's important to keep the logical FLTK coordinate system intact. - Converting Cocoa coordinates into FLTK coordinates is not reliable because - it loses precision if the screen scale is set to anything but 1:1. - - See also: - Fl_Cocoa_Window_Driver::driver(window)->view_resized() avoid recursion - Fl_Cocoa_Window_Driver::driver(window)->through_resize(); avoid recursion - Fl_Cocoa_Window_Driver::driver(window)->changed_resolution(); tested OK - */ -- (void)view_did_resize:(NSNotification *)notif -{ - if (![[notif object] isKindOfClass:[FLView class]]) return; - FLView *view = (FLView*)[notif object]; - FLWindow *nsw = (FLWindow*)[view window]; - if (!nsw || ![nsw getFl_Window]) return; - fl_lock_function(); - Fl_Window *window = [nsw getFl_Window]; - - int X, Y, W, H; - float s = Fl::screen_driver()->scale(window->screen_num()); - if (Fl_Window::is_a_rescale()) { - if (window->parent()) { - X = window->x(); - Y = window->y(); - } else { - // Recalculate the FLTK position from the current Cocoa position applying - // the new scale, so the window stays at its current position after scaling. - CocoatoFLTK(window, X, Y); - } - W = window->w(); - H = window->h(); - } else if (Fl_Cocoa_Window_Driver::driver(window)->through_resize()) { - if (window->parent()) { - X = window->x(); - Y = window->y(); - } else { - // Recalculate the FLTK position from the current Cocoa position - CocoatoFLTK(window, X, Y); - } - W = window->w(); - H = window->h(); - } else { - CocoatoFLTK(window, X, Y); - NSRect r = [view frame]; - W = (int)lround(r.size.width/s); - H = (int)lround(r.size.height/s); - } - - Fl_Cocoa_Window_Driver::driver(window)->view_resized(1); - if (Fl_Cocoa_Window_Driver::driver(window)->through_resize()) { - if (window->as_gl_window()) { - static Fl_Cocoa_Plugin *plugin = NULL; - if (!plugin) { - Fl_Plugin_Manager pm("fltk:cocoa"); - plugin = (Fl_Cocoa_Plugin*)pm.plugin("gl.cocoa.fltk.org"); - } - // calls Fl_Gl_Window::resize() without including Fl_Gl_Window.H - plugin->resize(window->as_gl_window(), X, Y, W, H); - } else { - Fl_Cocoa_Window_Driver::driver(window)->resize(X, Y, W, H); - } - } else - window->resize(X, Y, W, H); - Fl_Cocoa_Window_Driver::driver(window)->view_resized(0); - update_e_xy_and_e_xy_root(nsw); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (views_use_CA && !window->as_gl_window()) { - [view reset_aux_bitmap]; - window->redraw(); - } -#endif - if (!window->parent() && window->border() && Fl_Window_Driver::driver(window)->is_resizable()) { - Fl_Cocoa_Window_Driver::driver(window)->is_maximized([nsw isZoomed] && - !window->fullscreen_active()); - } - fl_unlock_function(); -} -- (void)windowDidResignKey:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - /* Fullscreen windows obscure all other windows so we need to return - to a "normal" level when the user switches to another window or another app */ - if (window->fullscreen_active()) { - [nsw setLevel:NSNormalWindowLevel]; - fixup_window_levels(); - } - // Situations such as opening a character palette produce windowDidResignKey but - // [NSApp keyWindow] remains set to the resigning window. In that case, don't send FL_UNFOCUS - if ([NSApp keyWindow] != nsw) Fl::handle(FL_UNFOCUS, window); - fl_unlock_function(); -} -- (void)windowDidBecomeKey:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *w = [nsw getFl_Window]; - /* Restore previous fullscreen level */ - if (w->fullscreen_active() && !(nsw.styleMask & NSWindowStyleMaskFullScreen)) { - [nsw setLevel:NSStatusWindowLevel]; - fixup_window_levels(); - } - Fl::handle( FL_FOCUS, w); - fl_unlock_function(); -} -- (void)windowDidBecomeMain:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - Fl::first_window(window); - if (!window->parent()) [nsw orderFront:nil]; - if (fl_sys_menu_bar && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style()) { - // select the corresponding Window menu item - int index = Fl_MacOS_Sys_Menu_Bar_Driver::driver()->first_window_menu_item; - while (index > 0) { - Fl_Menu_Item *item = Fl_MacOS_Sys_Menu_Bar_Driver::driver()->window_menu_items + index; - if (!item->label()) break; - if (item->user_data() == window) { - if (!item->value()) { - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->setonly(item); - } - break; - } - index++; - } - } - fl_unlock_function(); -} -- (void)windowDidDeminiaturize:(NSNotification *)notif -{ - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - if ([nsw miniwindowImage]) { [nsw setMiniwindowImage:nil]; } - Fl_Window *window = [nsw getFl_Window]; - Fl::handle(FL_SHOW, window); - // necessary when resolutions before miniaturization and after deminiaturization differ - // or if GUI was resized while window was minimized - [nsw recursivelySendToSubwindows:@selector(setSubwindowFrame) applyToSelf:YES]; - update_e_xy_and_e_xy_root(nsw); - Fl::flush(); // Process redraws set by FL_SHOW. - fl_unlock_function(); -} -- (void)fl_windowMiniaturize:(NSNotification *)notif -{ - // subwindows are not captured in system-built miniature window image - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - if ([[nsw childWindows] count]) { - Fl_Window *window = [nsw getFl_Window]; - // capture the window and its subwindows and use as miniature window image - NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep_subwins(window, 0, 0, window->w(), window->h(), true); - if (bitmap) { - NSImage *img = [[[NSImage alloc] initWithSize:NSMakeSize([bitmap pixelsWide], [bitmap pixelsHigh])] autorelease]; - [img addRepresentation:bitmap]; - [bitmap release]; - [nsw setMiniwindowImage:img]; - } - } - fl_unlock_function(); -} -- (void)windowDidMiniaturize:(NSNotification *)notif -{ - [self fl_windowMiniaturize:notif]; - fl_lock_function(); - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - Fl::handle(FL_HIDE, window); - fl_unlock_function(); -} -- (void)windowWillEnterFullScreen:(NSNotification *)notif -{ - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - window->_set_fullscreen(); -} -- (void)windowWillExitFullScreen:(NSNotification *)notif -{ - FLWindow *nsw = (FLWindow*)[notif object]; - Fl_Window *window = [nsw getFl_Window]; - window->_clear_fullscreen(); -} -- (BOOL)windowShouldClose:(id)fl -{ - fl_lock_function(); - Fl_Window *win = [(FLWindow *)fl getFl_Window]; - if (win) Fl::handle(FL_CLOSE, win); // this might or might not close the window - fl_unlock_function(); - // the system doesn't need to send [fl close] because FLTK does it when needed - return NO; -} -- (void)anyWindowWillClose:(NSNotification *)notif -{ - fl_lock_function(); - if ([[notif object] isKeyWindow]) { - // If the closing window is the key window, - // find a bordered top-level window to become the new key window - Fl_Window *w = Fl::first_window(); - while (w && (w->parent() || !w->border() || !w->visible())) { - w = Fl::next_window(w); - } - if (w) { - [fl_mac_xid(w) makeKeyWindow]; - } - } - fl_unlock_function(); -} -- (void)doNothing:(id)unused -{ - return; -} -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu { - return NO; -} -@end - -@interface FLAppDelegate : NSObject <NSApplicationDelegate> -{ - @public - open_cb_f_type open_cb; - TSMDocumentID currentDoc; -} -- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app; -- (void)applicationDidFinishLaunching:(NSNotification *)notification; -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender; -- (void)applicationDidBecomeActive:(NSNotification *)notify; -- (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification; -- (void)applicationDidUpdate:(NSNotification *)aNotification; -- (void)applicationWillResignActive:(NSNotification *)notify; -- (void)applicationWillHide:(NSNotification *)notify; -- (void)applicationWillUnhide:(NSNotification *)notify; -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename; -@end - -@implementation FLAppDelegate -- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app { - // Avoids macOS 14 warning message when app is launched from command line: - // "WARNING: Secure coding is automatically enabled for restorable state! - // However, not on all supported macOS versions of this application. - // Opt-in to secure coding explicitly by implementing - // NSApplicationDelegate.applicationSupportsSecureRestorableState:." - return (fl_mac_os_version >= 140000); -} -- (void)applicationDidFinishLaunching:(NSNotification *)notification -{ - if (fl_mac_os_version >= 101300 && [NSApp isRunning]) [NSApp stop:nil]; -} -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender -{ - fl_lock_function(); - while ( Fl_X::first ) { - Fl_Window *win = Fl::first_window(); - if (win->parent()) win = win->top_window(); - Fl_Widget_Tracker wt(win); // track the window object - Fl::handle(FL_CLOSE, win); - if (wt.exists() && win->shown()) { // the user didn't close win - break; - } - } - fl_unlock_function(); - if ( ! Fl::first_window() ) { - Fl::program_should_quit(1); - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); // necessary when called through menu and in Fl::wait() - } - return NSTerminateCancel; -} -- (void)applicationDidBecomeActive:(NSNotification *)notify -{ - fl_lock_function(); - - // update clipboard status - clipboard_check(); - - /** - * Cocoa organizes the Z depth of windows on a global priority. FLTK however - * expects the window manager to organize Z level by application. The trickery - * below will change Z order during activation and deactivation. - */ - fixup_window_levels(); - - Fl::handle(FL_APP_ACTIVATE, nullptr); - fl_unlock_function(); -} -- (void)applicationDidChangeScreenParameters:(NSNotification *)unused -{ // react to changes in screen numbers and positions - fl_lock_function(); - main_screen_height = CGDisplayBounds(CGMainDisplayID()).size.height; - Fl::call_screen_init(); - Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL); - fl_unlock_function(); -} -- (void)applicationDidUpdate:(NSNotification *)aNotification -{ - if (im_enabled != -1) { - TSMDocumentID newDoc; - // It is extremely unclear when Cocoa decides to create/update - // the input context, but debugging reveals that it is done - // by NSApplication:updateWindows. So check if the input context - // has shifted after each such run so that we can update our - // input methods status. - newDoc = TSMGetActiveDocument(); - if (newDoc != currentDoc) { - TSMDocumentID doc; - - doc = TSMGetActiveDocument(); - - if (im_enabled) - TSMRemoveDocumentProperty(doc, kTSMDocumentEnabledInputSourcesPropertyTag); - else { - CFArrayRef inputSources; - CFDictionaryRef filter; - // FLΤΚ previously used TISCreateASCIICapableInputSourceList(), - // which mostly hits the mark. But it excludes things like Greek - // and Cyrillic keyboards. So let's be more explicit. - filter = CFDictionaryCreate(NULL, (const void **)kTISPropertyInputSourceType, - (const void **)kTISTypeKeyboardLayout, - 1, NULL, NULL); - inputSources = TISCreateInputSourceList(filter, false); - CFRelease(filter); - TSMSetDocumentProperty(doc, kTSMDocumentEnabledInputSourcesPropertyTag, - sizeof(CFArrayRef), &inputSources); - CFRelease(inputSources); - } - currentDoc = newDoc; - } - } -} -- (void)applicationWillResignActive:(NSNotification *)notify -{ - fl_lock_function(); - Fl_X *x; - FLWindow *top = 0; - // sort in all regular windows - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (win && cw) { - if (win->modal()) { - } else if (win->non_modal()) { - } else { - if (!top) top = cw; - } - } - } - // now sort in all modals - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (win && cw && [cw isVisible]) { - if (win->modal()) { - [cw setLevel:NSNormalWindowLevel]; - if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]]; - } - } - } - // finally all non-modals - for (x = Fl_X::first;x;x = x->next) { - FLWindow *cw = (FLWindow*)x->xid; - Fl_Window *win = x->w; - if (win && cw && [cw isVisible]) { - if (win->non_modal()) { - [cw setLevel:NSNormalWindowLevel]; - if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]]; - } - } - } - Fl::handle(FL_APP_DEACTIVATE, nullptr); - fl_unlock_function(); -} -- (void)applicationWillHide:(NSNotification *)notify -{ - fl_lock_function(); - Fl_X *x; - for (x = Fl_X::first;x;x = x->next) { - Fl_Window *window = x->w; - if ( !window->parent() ) Fl::handle( FL_HIDE, window); - } - fl_unlock_function(); -} -- (void)applicationWillUnhide:(NSNotification *)notify -{ - fl_lock_function(); - for (Fl_X *x = Fl_X::first;x;x = x->next) { - Fl_Window *w = x->w; - if ( !w->parent() && ![(FLWindow*)x->xid isMiniaturized]) { - Fl::handle(FL_SHOW, w); - } - } - fl_unlock_function(); -} -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename -{ - if (fl_mac_os_version < 101300) { - // without the next two statements, the opening of the 1st window is delayed by several seconds - // under 10.8 ≤ Mac OS < 10.13 when a file is dragged on the application icon - Fl_Window *firstw = Fl::first_window(); - if (firstw) firstw->wait_for_expose(); - } else if (in_nsapp_run) { // memorize all dropped filenames - if (!dropped_files_list) dropped_files_list = [[NSMutableArray alloc] initWithCapacity:1]; - [dropped_files_list addObject:filename]; - return YES; - } - if (open_cb) { - fl_lock_function(); - (*open_cb)([filename UTF8String]); - Fl::flush(); // useful for AppleScript that does not break the event loop - fl_unlock_function(); - return YES; - } - return NO; -} -@end - - -static void drain_dropped_files_list() { - open_cb_f_type open_cb = ((FLAppDelegate*)[NSApp delegate])->open_cb; - if (!open_cb) { - [dropped_files_list removeAllObjects]; - [dropped_files_list release]; - dropped_files_list = nil; - return; - } - NSString *s = (NSString*)[dropped_files_list objectAtIndex:0]; - char *fname = fl_strdup([s UTF8String]); - [dropped_files_list removeObjectAtIndex:0]; - if ([dropped_files_list count] == 0) { - [dropped_files_list release]; - dropped_files_list = nil; - } - open_cb(fname); - free(fname); -} - -/* - * Install an open documents event handler... - */ -void Fl_Darwin_System_Driver::open_callback(void (*cb)(const char *)) { - fl_open_display(); - ((FLAppDelegate*)[NSApp delegate])->open_cb = cb; -} - -@implementation FLApplication -+ (void)sendEvent:(NSEvent *)theEvent -{ - if (fl_send_system_handlers(theEvent)) - return; - - NSEventType type = [theEvent type]; - if (type == NSEventTypeLeftMouseDown) { - fl_lock_function(); - Fl_Window *grab = Fl::grab(); - if (grab) { - FLWindow *win = (FLWindow *)[theEvent window]; - if ( [win isKindOfClass:[FLWindow class]] && grab != [win getFl_Window]) { - // a click event out of a menu window, so we should close this menu - // done here to catch also clicks on window title bar/resize box - cocoaMouseHandler(theEvent); - } - } - fl_unlock_function(); - } else if (type == NSEventTypeApplicationDefined) { - if ([theEvent subtype] == FLTKDataReadyEvent) { - processFLTKEvent(); - } - return; - } else if (type == NSEventTypeKeyUp) { - // The default sendEvent turns key downs into performKeyEquivalent when - // modifiers are down, but swallows the key up if the modifiers include - // command. This one makes all modifiers consistent by always sending key ups. - // FLView treats performKeyEquivalent to keyDown, but performKeyEquivalent is - // still needed for the system menu. - [[NSApp keyWindow] sendEvent:theEvent]; - return; - } - [NSApp sendEvent:theEvent]; -} -@end - -/* Prototype of undocumented function needed to support Mac OS 10.2 or earlier - extern "C" { - OSErr CPSEnableForegroundOperation(ProcessSerialNumber*, UInt32, UInt32, UInt32, UInt32); -} -*/ - -static BOOL is_bundled() { - static int value = 2; - if (value == 2) { - value = 1; - NSBundle *bundle = [NSBundle mainBundle]; - if (bundle) { - NSString *exe = [[bundle executablePath] stringByStandardizingPath]; - NSString *bpath = [[bundle bundlePath] stringByStandardizingPath]; - NSString *exe_dir = [exe stringByDeletingLastPathComponent]; -//NSLog(@"exe=%@ bpath=%@ exe_dir=%@",exe, bpath, exe_dir); - if ([bpath isEqualToString:exe] || [bpath isEqualToString:exe_dir]) value = 0; - } else value = 0; - } - return value == 1; -} - - -static void foreground_and_activate() { - if ( !is_bundled() ) { // only transform the application type for unbundled apps - ProcessSerialNumber cur_psn = { 0, kCurrentProcess }; - TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication); // needs Mac OS 10.3 - /* support of Mac OS 10.2 or earlier used this undocumented call instead - err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103); - */ - } - [NSApp activateIgnoringOtherApps:YES]; -} - -// simpler way to activate application tested OK on MacOS 10.3 10.6 10.9 10.13 and 10.14 public beta - -void Fl_Cocoa_Screen_Driver::open_display_platform() { - static char beenHereDoneThat = 0; - if ( !beenHereDoneThat ) { - beenHereDoneThat = 1; - - BOOL need_new_nsapp = (NSApp == nil); - if (need_new_nsapp) [NSApplication sharedApplication]; - NSAutoreleasePool *localPool; - localPool = [[NSAutoreleasePool alloc] init]; // never released - FLAppDelegate *delegate = [FLAppDelegate alloc]; - [(NSApplication*)NSApp setDelegate:[delegate init]]; - if (need_new_nsapp) { - if (fl_mac_os_version >= 101300 && fl_mac_os_version < 140000 && is_bundled()) { - [NSApp activateIgnoringOtherApps:YES]; - in_nsapp_run = true; - [NSApp run]; - in_nsapp_run = false; - } - else { - [NSApp finishLaunching]; - // Unbundled app may require this so delegate receives applicationDidFinishLaunching: - // even if doc states this is sent at the end of finishLaunching. - if (!is_bundled()) [NSApp nextEventMatchingMask:NSEventMaskAny - untilDate:nil - inMode:NSDefaultRunLoopMode - dequeue:NO]; - } - } - if (fl_mac_os_version < 140000) { - // empty the event queue but keep system events for drag&drop of files at launch - NSEvent *ign_event; - do ign_event = [NSApp nextEventMatchingMask:(NSEventMaskAny & ~NSEventMaskSystemDefined) - untilDate:[NSDate dateWithTimeIntervalSinceNow:0] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - while (ign_event); - } - if (![NSApp isActive]) foreground_and_activate(); - if (![NSApp servicesMenu]) createAppleMenu(); - else Fl_Sys_Menu_Bar::window_menu_style(Fl_Sys_Menu_Bar::no_window_menu); - main_screen_height = CGDisplayBounds(CGMainDisplayID()).size.height; - [[NSNotificationCenter defaultCenter] addObserver:[FLWindowDelegate singleInstance] - selector:@selector(anyWindowWillClose:) - name:NSWindowWillCloseNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:[FLWindowDelegate singleInstance] - selector:@selector(view_did_resize:) - name:NSViewFrameDidChangeNotification - object:nil]; - if (![NSThread isMultiThreaded]) { - // With old OS X versions, it is necessary to create one thread for secondary pthreads to be - // allowed to use cocoa, especially to create an NSAutoreleasePool. - // We create a thread that does nothing so it completes very fast: - [NSThread detachNewThreadSelector:@selector(doNothing:) toTarget:[FLWindowDelegate singleInstance] withObject:nil]; - } - (void)localPool; // silence warning - } -} - - -// Force a "Roman" or "ASCII" keyboard, which both the Mozilla and -// Safari people seem to think implies turning off advanced IME stuff -// (see nsTSMManager::SyncKeyScript in Mozilla and enableSecureTextInput -// in Safari/Webcore). Should be good enough for us then... - -static int input_method_startup() -{ - static int retval = -1; // -1: not initialized, 0: not usable, 1: ready for use - if (retval == -1) { - fl_open_display(); - // These symbols require 10.5. They are no longer visible in Apple doc. - // They do exist in Carbon.framework --> HIToolbox.framework --> TextServices.h - TSMGetActiveDocument = (TSMGetActiveDocument_type)Fl_Darwin_System_Driver::get_carbon_function("TSMGetActiveDocument"); - TSMSetDocumentProperty = (TSMSetDocumentProperty_type)Fl_Darwin_System_Driver::get_carbon_function("TSMSetDocumentProperty"); - TSMRemoveDocumentProperty = (TSMRemoveDocumentProperty_type)Fl_Darwin_System_Driver::get_carbon_function("TSMRemoveDocumentProperty"); - // These symbols are no longer visible in Apple doc. - // They do exist in Carbon.framework --> HIToolbox.framework --> TextInputSources.h - TISCreateInputSourceList = (TISCreateInputSourceList_type)Fl_Darwin_System_Driver::get_carbon_function("TISCreateInputSourceList"); - kTISTypeKeyboardLayout = (CFStringRef)Fl_Darwin_System_Driver::get_carbon_function("kTISTypeKeyboardLayout"); - kTISPropertyInputSourceType = (CFStringRef)Fl_Darwin_System_Driver::get_carbon_function("kTISPropertyInputSourceType"); - retval = (TSMGetActiveDocument && TSMSetDocumentProperty && TSMRemoveDocumentProperty && TISCreateInputSourceList && kTISTypeKeyboardLayout && kTISPropertyInputSourceType ? 1 : 0); - } - return retval; -} - -void Fl_Cocoa_Screen_Driver::enable_im() { - if (!input_method_startup()) return; - - im_enabled = 1; - - ((FLAppDelegate*)[NSApp delegate])->currentDoc = NULL; - [NSApp updateWindows]; // triggers [FLAppDelegate applicationDidUpdate] -} - -void Fl_Cocoa_Screen_Driver::disable_im() { - if (!input_method_startup()) return; - - im_enabled = 0; - - ((FLAppDelegate*)[NSApp delegate])->currentDoc = NULL; - [NSApp updateWindows]; // triggers [FLAppDelegate applicationDidUpdate] -} - - -// Gets the border sizes and the titlebar height -static int get_window_frame_sizes(Fl_Window *win, int *pbx, int *pby) { - if (pbx) *pbx = 0; if (pby) *pby = 0; - if (win && !win->border()) return 0; - FLWindow *flw = fl_xid(win); - if (flw) { - return [flw frame].size.height - [[flw contentView] frame].size.height; - } - static int top = 0, left, bottom; - if (!top) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSRect inside = { {20,20}, {100,100} }; - NSRect outside = [NSWindow frameRectForContentRect:inside - styleMask:NSWindowStyleMaskTitled]; - left = int(outside.origin.x - inside.origin.x); - bottom = int(outside.origin.y - inside.origin.y); - top = int(outside.size.height - inside.size.height) - bottom; - [pool release]; - } - if (pbx) *pbx = left; - if (pby) *pby = bottom; - return top; -} - -void Fl_Cocoa_Window_Driver::decoration_sizes(int *top, int *left, int *right, int *bottom) { - *top = get_window_frame_sizes(pWindow, left, bottom); - *right = *left; -} - -/* - * smallest x coordinate in screen space of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::x() { - open_display(); - return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].origin.x) / scale(0); -} - - -/* - * smallest y coordinate in screen space of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::y() { - open_display(); - NSRect visible = [[[NSScreen screens] objectAtIndex:0] visibleFrame]; - return int(main_screen_height - (visible.origin.y + visible.size.height)) / scale(0); -} - - -/* - * width of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::w() { - open_display(); - return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].size.width) / scale(0); -} - - -/* - * height of work area of menubar-containing display - */ -int Fl_Cocoa_Screen_Driver::h() { - open_display(); - return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].size.height) / scale(0); -} - -// computes the work area of the nth screen (screen #0 has the menubar) -void Fl_Cocoa_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n) -{ - if (num_screens < 0) init(); - if (n < 0 || n >= num_screens) n = 0; - open_display(); - NSRect r = [[[NSScreen screens] objectAtIndex:n] visibleFrame]; - X = int(r.origin.x) / scale(0); - Y = (main_screen_height - int(r.origin.y + r.size.height)) / scale(0); - W = int(r.size.width) / scale(0); - H = int(r.size.height) / scale(0); -} - -/* - * get the current mouse pointer world coordinates - */ -int Fl_Cocoa_Screen_Driver::get_mouse(int &x, int &y) -{ - open_display(); - NSPoint pt = [NSEvent mouseLocation]; - x = int(pt.x); - y = int(main_screen_height - pt.y); - return screen_num(x/scale(0), y/scale(0)); -} - - -static int fake_X_wm(Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) { - int W, H, xoff, yoff, dx, dy; - int ret = bx = by = bt = 0; - if (w->border() && !w->parent()) { - int minw, minh, maxw, maxh; - w->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - if (maxw != minw || maxh != minh) { - ret = 2; - } else { - ret = 1; - } - bt = get_window_frame_sizes(w, &bx, &by); - } - if (w->parent()) return 0; - // The coordinates of the whole window, including non-client area - xoff = bx; - yoff = by + bt; - dx = 2*bx; - dy = 2*by + bt; - float s = Fl::screen_driver()->scale(0); - X = round(w->x()*s)-xoff; - Y = round(w->y()*s)-yoff; - W = w->w()*s+dx; - H = w->h()*s+dy; - - // Proceed to positioning the window fully inside the screen, if possible - - // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk - // that we want to avoid when positioning our window, namely the Dock and the - // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the - // list of all available screens and find the one that this window is most - // likely to go to, and then reposition it to fit withing the 'good' area. - // Rect r; - // find the screen, that the center of this window will fall into - int R = X+W, B = Y+H; // right and bottom - int cx = (X+R)/2, cy = (Y+B)/2; // center of window; - NSScreen *gd = NULL; - NSArray *a = [NSScreen screens]; int count = (int)[a count]; NSRect r; int i; - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( cx >= r.origin.x && cx <= r.origin.x + r.size.width - && cy >= r.origin.y && cy <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - - // if the center doesn't fall on a screen, try the top left - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( X >= r.origin.x && X <= r.origin.x + r.size.width - && Y >= r.origin.y && Y <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // if that doesn't fall on a screen, try the top right - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( R >= r.origin.x && R <= r.origin.x + r.size.width - && Y >= r.origin.y && Y <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // if that doesn't fall on a screen, try the bottom left - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( X >= r.origin.x && X <= r.origin.x + r.size.width - && Y+H >= r.origin.y && Y+H <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // last resort, try the bottom right - if (!gd) { - for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( R >= r.origin.x && R <= r.origin.x + r.size.width - && Y+H >= r.origin.y && Y+H <= r.origin.y + r.size.height) - break; - } - if (i < count) gd = [a objectAtIndex:i]; - } - // if we still have not found a screen, we will use the main - // screen, the one that has the application menu bar. - if (!gd) gd = [a objectAtIndex:0]; - if (gd) { - r = [gd visibleFrame]; - r.origin.y = main_screen_height - (r.origin.y + r.size.height); // use FLTK's multiscreen coordinates - if ( R > r.origin.x + r.size.width ) X -= int(R - (r.origin.x + r.size.width)); - if ( B > r.size.height + r.origin.y ) Y -= int(B - (r.size.height + r.origin.y)); - if ( X < r.origin.x ) X = int(r.origin.x); - if ( Y < r.origin.y ) Y = int(r.origin.y); - } - - // Return the client area's top left corner in (X,Y) - X+=xoff; - Y+=yoff; - X /= s; - Y /= s; - - return ret; -} - - -Fl_Window *fl_dnd_target_window = 0; - -static void q_set_window_title(NSWindow *nsw, const char * name, const char *mininame) { - CFStringRef title = CFStringCreateWithCString(NULL, (name ? name : ""), kCFStringEncodingUTF8); - if(!title) { // fallback when name contains malformed UTF-8 - int l = (int)strlen(name); - unsigned short* utf16 = new unsigned short[l + 1]; - l = fl_utf8toUtf16(name, l, utf16, l + 1); - title = CFStringCreateWithCharacters(NULL, utf16, l); - delete[] utf16; - } - [nsw setTitle:(NSString*)title]; - CFRelease(title); - if (mininame && strlen(mininame)) { - CFStringRef minititle = CFStringCreateWithCString(NULL, mininame, kCFStringEncodingUTF8); - if (minititle) { - [nsw setMiniwindowTitle:(NSString*)minititle]; - CFRelease(minititle); - } - } -} - -/** How FLTK handles Mac OS text input - - Let myview be the instance of the FLView class that has the keyboard focus. FLView is an FLTK-defined NSView subclass - that implements the NSTextInputClient protocol to properly handle text input. It also implements the old NSTextInput - protocol to run with OS <= 10.4. The few NSTextInput protocol methods that differ in signature from the NSTextInputClient - protocol transmit the received message to the corresponding NSTextInputClient method. - - Keyboard input sends keyDown: and performKeyEquivalent: messages to myview. The latter occurs for keys such as - ForwardDelete, arrows and F1, and when the Ctrl or Cmd modifiers are used. Other key presses send keyDown: messages. - The keyDown: method calls [myview process_keydown:theEvent] that is equivalent to - [[myview inputContext] handleEvent:theEvent], and triggers system processing of keyboard events. - The performKeyEquivalent: method directly calls Fl::handle(FL_KEYBOARD, focus-window) - when the Ctrl or Cmd modifiers are used. If not, it also calls [[myview inputContext] handleEvent:theEvent]. - The performKeyEquivalent: method returns YES when the keystroke has been handled and NO otherwise, which allows - shortcuts of the system menu to be processed. Three sorts of messages are then sent back by the system to myview: - doCommandBySelector:, setMarkedText: and insertText:. All 3 messages eventually produce Fl::handle(FL_KEYBOARD, win) calls. - The doCommandBySelector: message allows to process events such as new-line, forward and backward delete, arrows, - escape, tab, F1. The message setMarkedText: is sent when marked text, that is, temporary text that gets replaced later - by some other text, is inserted. This happens when a dead key is pressed, and also - when entering complex scripts (e.g., Chinese). Fl_Cocoa_Screen_Driver::next_marked_length gives the byte - length of marked text before the FL_KEYBOARD event is processed. Fl::compose_state gives this length after this processing. - Message insertText: is sent to enter text in the focused widget. If there's marked text, Fl::compose_state is > 0, and this - marked text gets replaced by the inserted text. If there's no marked text, the new text is inserted at the insertion point. - When the character palette is used to enter text, the system sends an insertText: message to myview. - The in_key_event field of the FLView class allows to differentiate keyboard from palette inputs. - - During processing of the handleEvent message, inserted and marked strings are concatenated in a single string - inserted in a single FL_KEYBOARD event after return from handleEvent. The need_handle member variable of FLView allows - to determine when setMarkedText or insertText strings have been sent during handleEvent processing and must trigger - an FL_KEYBOARD event. Concatenating two insertText operations or an insertText followed by a setMarkedText is possible. - In contrast, setMarkedText followed by insertText or by another setMarkedText isn't correct if concatenated in a single - string. Thus, in such case, the setMarkedText and the next operation produce each an FL_KEYBOARD event. - - OS >= 10.7 contains a feature where pressing and holding certain keys opens a menu window that shows a list - of possible accented variants of this key. The selectedRange field of the FLView class and the selectedRange, insertText: - and setMarkedText: methods of the NSTextInputClient protocol are used to support this feature. - The notion of selected text (!= marked text) is monitored by the selectedRange field. - The -(NSRange)[FLView selectedRange] method is used to control whether an FLTK widget opens accented character windows - by returning .location = NSNotFound to disable that, or returning the value of the selectedRange field to enable the feature. - When selectedRange.location >= 0, the value of selectedRange.length is meaningful. 0 means no text is currently selected, - > 0 means this number of characters before the insertion point are selected. The insertText: method does - selectedRange = NSMakeRange(100, 0); to indicate no text is selected. The setMarkedText: method does - selectedRange = NSMakeRange(100, newSelection.length); to indicate that this length of text is selected. - - With OS <= 10.5, the NSView class does not implement the inputContext message. [myview process_keydown:theEvent] is - equivalent to [[FLTextInputContext singleInstance] handleEvent:theEvent]. - Method +[FLTextInputContext singleInstance] returns an instance of class FLTextInputContext that possesses - a handleEvent: method. The class FLTextView implements the so-called view's "field editor". This editor is an instance - of the FLTextView class allocated by the -(id)[FLWindowDelegate windowWillReturnFieldEditor: toObject:] method. - The -(BOOL)[FLTextInputContext handleEvent:] method emulates the missing 10.6 -(BOOL)[NSTextInputContext handleEvent:] - by sending the interpretKeyEvents: message to the FLTextView object. The system sends back doCommandBySelector: and - insertText: messages to the FLTextView object that are transmitted unchanged to myview to be processed as with OS >= 10.6. - The system also sends setMarkedText: messages directly to myview. - - There is furthermore an oddity of dead key processing with OS <= 10.5. It occurs when a dead key followed by a non-accented - key are pressed. Say, for example, that keys '^' followed by 'p' are pressed on a French or German keyboard. Resulting - messages are: [myview setMarkedText:@"^"], [myview insertText:@"^"], [myview insertText:@"p"], [FLTextView insertText:@"^p"]. - The 2nd '^' replaces the marked 1st one, followed by p^p. The resulting text in the widget is "^p^p" instead of the - desired "^p". To avoid that, the FLTextView object is deactivated by the insertText: message and reactivated after - the handleEvent: message has been processed. - - NSEvent's during a character composition sequence: - - keyDown with deadkey -> [[theEvent characters] length] is 0 - - keyUp -> [theEvent characters] contains the deadkey - - keyDown with next key -> [theEvent characters] contains the composed character - - keyUp -> [theEvent characters] contains the standard character - */ - -static void cocoaKeyboardHandler(NSEvent *theEvent) -{ - NSUInteger mods; - // get the modifiers - mods = [theEvent modifierFlags]; - // get the key code - UInt32 keyCode = 0, maskedKeyCode = 0; - unsigned short sym = 0; - keyCode = [theEvent keyCode]; - // extended keyboards can also send sequences on key-up to generate Kanji etc. codes. - // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode. - // In this mode, there seem to be no key-down codes - // printf("%08x %08x %08x\n", keyCode, mods, key); - maskedKeyCode = keyCode & 0x7f; - mods_to_e_state( mods ); // process modifier keys - if (!macKeyLookUp) macKeyLookUp = Fl_Darwin_System_Driver::compute_macKeyLookUp(); - sym = macKeyLookUp[maskedKeyCode]; - if (sym < 0xff00) { // a "simple" key - // find the result of this key without modifier - NSString *sim = [theEvent charactersIgnoringModifiers]; - UniChar one; - CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one); - // charactersIgnoringModifiers doesn't ignore shift, remove it when it's on - if(one >= 'A' && one <= 'Z') one += 32; - if (one > 0 && one <= 0x7f && (sym<'0' || sym>'9') ) sym = one; - } - Fl::e_keysym = Fl::e_original_keysym = sym; - /*NSLog(@"cocoaKeyboardHandler: keycode=%08x keysym=%08x mods=%08x symbol=%@ (%@)", - keyCode, sym, mods, [theEvent characters], [theEvent charactersIgnoringModifiers]);*/ - // If there is text associated with this key, it will be filled in later. - Fl::e_length = 0; - Fl::e_text = (char*)""; -} - -@interface FLTextInputContext : NSObject // "emulates" NSTextInputContext before OS 10.6 -+ (void)initialize; -+ (FLTextInputContext*)singleInstance; --(BOOL)handleEvent:(NSEvent*)theEvent; -@end -static FLTextInputContext* fltextinputcontext_instance = nil; -@implementation FLTextInputContext -+ (void)initialize { - fltextinputcontext_instance = [[FLTextInputContext alloc] init]; -} -+ (FLTextInputContext*)singleInstance { - return fltextinputcontext_instance; -} --(BOOL)handleEvent:(NSEvent*)theEvent { - FLTextView *edit = [FLTextView singleInstance]; - [edit setActive:YES]; - [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; - [edit setActive:YES]; - return YES; -} -@end - -/* Implementation note for the support of layer-backed views. - MacOS 10.14 Mojave changes the way all drawing to displays is performed: - all NSView objects become layer-backed, that is, drawing is done by - Core Animation to a CALayer object whose content is then displayed by the NSView. - The global variable views_use_CA is set to YES when such change applies, - that is, for apps running under 10.14 and linked to SDK 10.14. - When views_use_CA is NO, views are not supposed to be layer-backed. - - Most drawing is done by [FLView drawRect:] which the system calls - when a window is created or resized and when Fl_Window_Driver::flush() runs which sends the display - message to the view. Within drawRect:, [[NSGraphicsContext currentContext] CGContext] - gives a graphics context whose product ultimately appears on screen. But the - full content of the view must be redrawn each time drawRect: runs, in contrast - to pre-10.14 where drawings were added to the previous window content. - That is why FLView maintains a bitmap (view->aux_bitmap) to which all drawing is directed. - At the end of drawRect:, the content of view->aux_bitmap is copied to the window's graphics context. - - A problem arises to support drawing done outside Fl_Window_Driver::flush(), that is, - after the app calls Fl_Window::make_current() at any time it wants. - That situation is identified by the condition (views_use_CA && !through_drawRect). - Fl_Window::make_current() thus calls [view setNeedsDisplay:YES] which instructs the system to - run drawRect: at the next event loop. Later, when drawRect: runs, the content of - aux_bitmap is copied to drawRect's graphics context. - - OpenGL windows remain processed under 10.14 as before. - */ - -@implementation FLView -- (BOOL)did_view_resolution_change { - // determine whether window is mapped to a retina display - Fl_Window *window = [(FLWindow*)[self window] getFl_Window]; - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); - bool previous = d->mapped_to_retina(); - NSView *view = (!views_use_CA && window->parent() && !window->as_gl_window()) ? - [fl_xid(window->top_window()) contentView] : self; - if (view) { - NSSize s = [view convertSizeToBacking:NSMakeSize(10, 10)]; // 10.7 - d->mapped_to_retina( int(s.width + 0.5) > 10 ); - } - BOOL retval = (d->wait_for_expose_value == 0 && previous != d->mapped_to_retina()); - if (retval) { - d->changed_resolution(true); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (views_use_CA && !window->as_gl_window() ) { - [self reset_aux_bitmap]; - window->redraw(); - } -#endif - } - return retval; -} -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -- (void)create_aux_bitmap:(CGContextRef)gc retina:(BOOL)r { - if (!gc || fl_mac_os_version >= 101600) { - // bitmap context-related functions (e.g., CGBitmapContextGetBytesPerRow) can't be used here with macOS 11.0 "Big Sur" - static CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB(); - int W = [self frame].size.width, H = [self frame].size.height; - if (r) { W *= 2; H *= 2; } - aux_bitmap = CGBitmapContextCreate(NULL, W, H, 8, 0, cspace, kCGImageAlphaPremultipliedFirst|kCGBitmapByteOrder32Host); - } else { - aux_bitmap = CGBitmapContextCreate(NULL, CGBitmapContextGetWidth(gc), CGBitmapContextGetHeight(gc), - CGBitmapContextGetBitsPerComponent(gc), CGBitmapContextGetBytesPerRow(gc), - CGBitmapContextGetColorSpace(gc), CGBitmapContextGetBitmapInfo(gc)); - } - CGContextClearRect(aux_bitmap, CGRectMake(0, 0, - CGBitmapContextGetWidth(aux_bitmap), CGBitmapContextGetHeight(aux_bitmap))); - if (r) CGContextScaleCTM(aux_bitmap, 2, 2); -} -- (void)reset_aux_bitmap { - CGContextRelease(aux_bitmap); - aux_bitmap = NULL; -} -#endif -- (BOOL)process_keydown:(NSEvent*)theEvent -{ - return [[self inputContext] handleEvent:theEvent]; -} -- (id)initWithFrame:(NSRect)frameRect -{ - static NSInteger counter = 0; - self = [super initWithFrame:frameRect]; - if (self) { - in_key_event = NO; - identifier = ++counter; - } - return self; -} - -/* Used by all GL or non-GL windows. - * Gets called when a window is created, resized, or moved between retina and non-retina displays. - * For non-GL windows, also called by Fl_Window_Driver::flush() because of the display message sent to the view. - */ -- (void)drawRect:(NSRect)rect -{ - FLWindow *cw = (FLWindow*)[self window]; - Fl_Window *window = [cw getFl_Window]; - if (!window) return; // may happen after closing full-screen window - if (!Fl_X::flx(window)) return; // reported to happen with Gmsh (issue #434) - fl_lock_function(); - Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(window); - if (!through_Fl_X_flush -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - && (!views_use_CA || !aux_bitmap) -#endif - ) { - [self did_view_resolution_change]; - if (d->wait_for_expose_value) { - d->wait_for_expose_value = 0; - if (window->as_gl_window() && views_use_CA && fl_mac_os_version < 101401) { // 1st drawing of layer-backed GL window - window->size(window->w(), window->h()); // sends message [GLcontext update] - } - } - Fl_X *i = Fl_X::flx(window); - if ( i->region ) { - Fl_Graphics_Driver::default_driver().XDestroyRegion(i->region); - i->region = 0; - } - window->clear_damage(FL_DAMAGE_ALL); - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - CGContextRef destination = NULL; - if (views_use_CA) { - destination = [[NSGraphicsContext currentContext] CGContext]; - if (!aux_bitmap && !window->as_gl_window()) [self create_aux_bitmap:destination retina:d->mapped_to_retina()]; - } -#endif - through_drawRect = YES; - if (window->damage()) d->Fl_Window_Driver::flush(); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (destination) { // can be NULL with gl_start/gl_finish - if (fl_mac_os_version < 101600 && CGBitmapContextGetBytesPerRow(aux_bitmap) == CGBitmapContextGetBytesPerRow(destination)) { - memcpy(CGBitmapContextGetData(destination), CGBitmapContextGetData(aux_bitmap), - CGBitmapContextGetHeight(aux_bitmap) * CGBitmapContextGetBytesPerRow(aux_bitmap)); - } else { - CGImageRef img = CGBitmapContextCreateImage(aux_bitmap); - CGContextDrawImage(destination, [self frame], img); - CGImageRelease(img); - } - } -#endif - Fl_Cocoa_Window_Driver::q_release_context(); - if (!through_Fl_X_flush) window->clear_damage(); - through_drawRect = NO; - fl_unlock_function(); -} - -- (BOOL)acceptsFirstResponder -{ - return [[self window] parentWindow] ? NO : YES; // 10.2 -} -- (BOOL)performKeyEquivalent:(NSEvent*)theEvent -{ - //NSLog(@"performKeyEquivalent:"); - /* The condition below is always false (and therefore the return statement doesn't run) - for the FLTK library unless it contains class Fl_Native_Input with which FLTK windows - may contain subviews inside their contentView. When such subview has focus, the condition - below becomes true. - */ - if ([[self window] firstResponder] != self) { - return NO; - } - NSUInteger mods = [theEvent modifierFlags]; - NSString *pure = [theEvent charactersIgnoringModifiers]; - // detect Function+e to open character palette - if ((mods & NSEventModifierFlagFunction) && [pure isEqualToString:@"e"] ) { - [NSApp orderFrontCharacterPalette:self]; - return YES; - } - fl_lock_function(); - cocoaKeyboardHandler(theEvent); - BOOL handled; - Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; - if ( (mods & NSEventModifierFlagControl) || (mods & NSEventModifierFlagCommand) ) { - NSString *s = [theEvent characters]; - if ( (mods & NSEventModifierFlagShift) && (mods & NSEventModifierFlagCommand) ) { - s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit - } - [FLView prepareEtext:s]; - Fl::compose_state = 0; - handled = Fl::handle(FL_KEYBOARD, w); - if (!handled) { - // detect Ctrl+Command+Space to open character palette, if not used before as shortcut - if ( (mods & NSEventModifierFlagControl) && (mods & NSEventModifierFlagCommand) && - !(mods & (NSEventModifierFlagShift|NSEventModifierFlagOption)) && [pure isEqualToString:@" "] ) { - [NSApp orderFrontCharacterPalette:self]; - } - } - } - else { - in_key_event = YES; - need_handle = NO; - handled = [self process_keydown:theEvent]; - if (need_handle) handled = Fl::handle(FL_KEYBOARD, w); - in_key_event = NO; - } - fl_unlock_function(); - return handled; -} -- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent -{ - Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; - Fl_Window *first = Fl::first_window(); - return (first == w || !first->modal()); -} -- (void)resetCursorRects { - Fl_Window *w = [(FLWindow*)[self window] getFl_Window]; - Fl_X *i = (w ? Fl_X::flx(w) : NULL); - if (!i) return; // fix for STR #3128 - // We have to have at least one cursor rect for invalidateCursorRectsForView - // to work, hence the "else" clause. - if (Fl_Cocoa_Window_Driver::driver(w)->cursor) - [self addCursorRect:[self frame] cursor:Fl_Cocoa_Window_Driver::driver(w)->cursor]; - else - [self addCursorRect:[self frame] cursor:[NSCursor arrowCursor]]; -} -- (void)mouseUp:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)rightMouseUp:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)otherMouseUp:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)mouseDown:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)rightMouseDown:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)otherMouseDown:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)mouseMoved:(NSEvent *)theEvent { - if (Fl::belowmouse()) cocoaMouseHandler(theEvent); -} -- (void)mouseEntered:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)mouseExited:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -#if defined(FLTK_HAVE_PEN_SUPPORT) -- (void)tabletProximity:(NSEvent *)theEvent { - cocoaTabletHandler(theEvent, true); -} -- (void)tabletPoint:(NSEvent *)theEvent { - cocoaTabletHandler(theEvent, true); -} -#endif -- (void)updateTrackingAreas { - if (![[self window] parentWindow]) { - while (true) { - NSArray *a = [self trackingAreas]; // 10.5 - if ([a count] == 0) break; - NSTrackingArea *ta = (NSTrackingArea*)[a objectAtIndex:0]; - [self removeTrackingArea:ta]; // 10.5 - } - NSTrackingArea *tracking = [[[NSTrackingArea alloc] // 10.5 - initWithRect:[self frame] - options:NSTrackingActiveAlways | - NSTrackingMouseEnteredAndExited | - NSTrackingMouseMoved - owner:self - userInfo:nil] autorelease]; - if (tracking) { - [self addTrackingArea:tracking]; // 10.5 - } - } - [super updateTrackingAreas]; -} -- (void)mouseDragged:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)rightMouseDragged:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)otherMouseDragged:(NSEvent *)theEvent { - cocoaMouseHandler(theEvent); -} -- (void)scrollWheel:(NSEvent *)theEvent { - cocoaMouseWheelHandler(theEvent); -} -- (void)magnifyWithEvent:(NSEvent *)theEvent { - cocoaMagnifyHandler(theEvent); -} -- (void)keyDown:(NSEvent *)theEvent { - //NSLog(@"keyDown:%@",[theEvent characters]); - fl_lock_function(); - Fl_Window *window = [(FLWindow*)[theEvent window] getFl_Window]; - Fl::first_window(window); - cocoaKeyboardHandler(theEvent); - in_key_event = YES; - Fl_Widget *f = Fl::focus(); - if (f && f->as_gl_window()) { // ignore text input methods for GL windows - need_handle = YES; - [FLView prepareEtext:[theEvent characters]]; - } else { - need_handle = NO; - [self process_keydown:theEvent]; - } - if (need_handle) Fl::handle(FL_KEYBOARD, window); - in_key_event = NO; - fl_unlock_function(); -} -- (void)keyUp:(NSEvent *)theEvent { - //NSLog(@"keyUp:%@",[theEvent characters]); - if (![[theEvent window] isKindOfClass:[FLWindow class]]) // issue #1170 - return [super keyUp:theEvent]; - fl_lock_function(); - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - Fl::first_window(window); - cocoaKeyboardHandler(theEvent); - NSString *s = [theEvent characters]; - if ([s length] >= 1) [FLView prepareEtext:[s substringToIndex:1]]; - Fl::handle(FL_KEYUP,window); - fl_unlock_function(); -} -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 - typedef NSUInteger NSEventModifierFlags; -#endif -- (void)flagsChanged:(NSEvent *)theEvent { - //NSLog(@"flagsChanged: "); - fl_lock_function(); - static NSEventModifierFlags prevMods = 0; - NSEventModifierFlags mods = [theEvent modifierFlags]; - Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - NSEventModifierFlags tMods = prevMods ^ mods; - int sendEvent = 0; - if ( tMods ) - { - unsigned short keycode = [theEvent keyCode]; - if (!macKeyLookUp) macKeyLookUp = Fl_Darwin_System_Driver::compute_macKeyLookUp(); - Fl::e_keysym = Fl::e_original_keysym = macKeyLookUp[keycode & 0x7f]; - if ( Fl::e_keysym ) - sendEvent = ( prevMods<mods ) ? FL_KEYBOARD : FL_KEYUP; - Fl::e_length = 0; - Fl::e_text = (char*)""; - prevMods = mods; - } - mods_to_e_state( mods ); - if (sendEvent) Fl::handle(sendEvent,window); - fl_unlock_function(); -} -- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender -{ - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - update_e_xy_and_e_xy_root([self window]); - fl_dnd_target_window = target; - int ret = Fl::handle( FL_DND_ENTER, target ); - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - Fl::flush(); - return ret ? NSDragOperationCopy : NSDragOperationNone; -} -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender -{ - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - update_e_xy_and_e_xy_root([self window]); - fl_dnd_target_window = target; - int ret = Fl::handle( FL_DND_DRAG, target ); - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - // if the DND started in the same application, Fl::dnd() will not return until - // the DND operation is finished. The call below causes the drop indicator - // to be drawn correctly (a full event handling would be better...) - Fl::flush(); - return ret ? NSDragOperationCopy : NSDragOperationNone; -} -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender -{ - static char *DragData = NULL; - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - if ( !Fl::handle( FL_DND_RELEASE, target ) ) { - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - return NO; - } - NSPasteboard *pboard; - // NSDragOperation sourceDragMask; - // sourceDragMask = [sender draggingSourceOperationMask]; - pboard = [sender draggingPasteboard]; - update_e_xy_and_e_xy_root([self window]); - if (DragData) { free(DragData); DragData = NULL; } - if ([[pboard types] containsObject:fl_filenames_pboard_type]) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - if (fl_mac_os_version >= 101300) { - NSArray *a = [pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] - options:nil]; // 10.6 - NSEnumerator *enumerator = [a objectEnumerator]; - NSURL *url; - while ((url = (NSURL*)[enumerator nextObject]) != nil) { - const char *p = [url fileSystemRepresentation]; // 10.9 - if (!DragData) { - DragData = strdup(p); - } else { - int l = (int)strlen(DragData) + (int)strlen(p) + 2; - char *drag2 = (char*)malloc(l); - snprintf(drag2, l, "%s\n%s", DragData, p); - free(DragData); - DragData = drag2; - } - } - } else -#endif - { - CFArrayRef files = (CFArrayRef)[pboard - propertyListForType:fl_filenames_pboard_type]; - CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n")); - int l = (int)CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), - kCFStringEncodingUTF8); - DragData = (char *)malloc(l + 1); - CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8); - CFRelease(all); - } - } else if ([[pboard types] containsObject:UTF8_pasteboard_type]) { - NSData *data = [pboard dataForType:UTF8_pasteboard_type]; - DragData = (char *)malloc([data length] + 1); - [data getBytes:DragData length:[data length]]; - DragData[([data length])] = 0; - Fl_Screen_Driver::convert_crlf(DragData, strlen(DragData)); - } - else { - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - return NO; - } - Fl::e_text = DragData; - Fl::e_length = (int)strlen(DragData); - int old_event = Fl::e_number; - Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); - Fl::e_number = old_event; - if (DragData) { free(DragData); DragData = NULL; } - Fl::e_text = NULL; - Fl::e_length = 0; - fl_dnd_target_window = NULL; - Fl_Cocoa_Screen_Driver::breakMacEventLoop(); - fl_unlock_function(); - return YES; -} -- (void)draggingExited:(id < NSDraggingInfo >)sender -{ - fl_lock_function(); - if ( fl_dnd_target_window ) { - Fl::handle( FL_DND_LEAVE, fl_dnd_target_window ); - fl_dnd_target_window = 0; - } - fl_unlock_function(); -} -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal -{ - return NSDragOperationGeneric; -} - -+ (void)prepareEtext:(NSString*)aString { - // fills Fl::e_text with UTF-8 encoded aString using an adequate memory allocation - static char *received_utf8 = NULL; - static int lreceived = 0; - char *p = (char*)[aString UTF8String]; - int l = (int)strlen(p); - if (l > 0) { - if (lreceived == 0) { - received_utf8 = (char*)malloc(l + 1); - lreceived = l; - } - else if (l > lreceived) { - received_utf8 = (char*)realloc(received_utf8, l + 1); - lreceived = l; - } - strcpy(received_utf8, p); - Fl::e_text = received_utf8; - } - Fl::e_length = l; -} - -+ (void)concatEtext:(NSString*)aString { - // extends Fl::e_text with aString - NSString *newstring = [[NSString stringWithUTF8String:Fl::e_text] stringByAppendingString:aString]; - [FLView prepareEtext:newstring]; -} - -- (void)doCommandBySelector:(SEL)aSelector { - NSString *s = [[NSApp currentEvent] characters]; - //NSLog(@"doCommandBySelector:%s text='%@'",sel_getName(aSelector), s); - s = [s substringFromIndex:[s length] - 1]; - [FLView prepareEtext:s]; // use the last character of the event; necessary for deadkey + Tab - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - Fl::handle(FL_KEYBOARD, target); -} - -- (void)insertText:(id)aString { - [self insertText:aString replacementRange:NSMakeRange(NSNotFound, 0)]; -} - -- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { - NSString *received; - if ([aString isKindOfClass:[NSAttributedString class]]) { - received = [(NSAttributedString*)aString string]; - } else { - received = (NSString*)aString; - } - /*NSLog(@"insertText='%@' l=%d Fl::compose_state=%d range=%d,%d", - received,strlen([received UTF8String]),Fl::compose_state,replacementRange.location,replacementRange.length);*/ - fl_lock_function(); - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - if (fl_mac_os_version >= 101400 && replacementRange.length > 0) { - // occurs after a key was pressed and maintained and an auxiliary window appeared - // prevents marking dead key from deactivation - [[self inputContext] discardMarkedText]; - } - while (replacementRange.length--) { // delete replacementRange.length characters before insertion point - int saved_keysym = Fl::e_keysym; - Fl::e_keysym = FL_BackSpace; - Fl::handle(FL_KEYBOARD, target); - Fl::e_keysym = saved_keysym; - } - if (in_key_event && Fl_Cocoa_Screen_Driver::next_marked_length && Fl::e_length) { - // if setMarkedText + insertText is sent during handleEvent, text cannot be concatenated in single FL_KEYBOARD event - Fl::handle(FL_KEYBOARD, target); - Fl::e_length = 0; - } - if (in_key_event && Fl::e_length) [FLView concatEtext:received]; - else [FLView prepareEtext:received]; - Fl_Cocoa_Screen_Driver::next_marked_length = 0; - // We can get called outside of key events (e.g., from the character palette, from CJK text input). - BOOL palette = !(in_key_event || Fl::compose_state); - if (palette) Fl::e_keysym = 0; - // YES if key has text attached - BOOL has_text_key = Fl::e_keysym <= '~' || Fl::e_keysym == FL_Iso_Key || - (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last && Fl::e_keysym != FL_KP_Enter); - // insertText sent during handleEvent of a key without text cannot be processed in a single FL_KEYBOARD event. - // Occurs with deadkey followed by non-text key. Occurs also with emoji palette. - if (!in_key_event || !has_text_key) { - Fl::handle(FL_KEYBOARD, target); - Fl::e_length = 0; - } - else need_handle = YES; - selectedRange = NSMakeRange(100, 0); // 100 is an arbitrary value - // for some reason, with the palette, the window does not redraw until the next mouse move or button push - // sending a 'redraw()' or 'awake()' does not solve the issue! - if (palette) Fl::flush(); - fl_unlock_function(); -} - -- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection { - [self setMarkedText:aString selectedRange:newSelection replacementRange:NSMakeRange(NSNotFound, 0)]; -} - -- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection replacementRange:(NSRange)replacementRange { - NSString *received; - if ([aString isKindOfClass:[NSAttributedString class]]) { - received = [(NSAttributedString*)aString string]; - } else { - received = (NSString*)aString; - } - fl_lock_function(); - /*NSLog(@"setMarkedText:%@ l=%d newSelection=%d,%d Fl::compose_state=%d replacement=%d,%d", - received, strlen([received UTF8String]), newSelection.location, newSelection.length, Fl::compose_state, - replacementRange.location, replacementRange.length);*/ - Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; - while (replacementRange.length--) { // delete replacementRange.length characters before insertion point - Fl::e_keysym = FL_BackSpace; - Fl::compose_state = 0; - Fl_Cocoa_Screen_Driver::next_marked_length = 0; - Fl::handle(FL_KEYBOARD, target); - Fl::e_keysym = 'a'; // pretend a letter key was hit - } - if (in_key_event && Fl_Cocoa_Screen_Driver::next_marked_length && Fl::e_length) { - // if setMarkedText + setMarkedText is sent during handleEvent, text cannot be concatenated in single FL_KEYBOARD event - Fl::handle(FL_KEYBOARD, target); - Fl::e_length = 0; - } - if (in_key_event && Fl::e_length) [FLView concatEtext:received]; - else [FLView prepareEtext:received]; - Fl_Cocoa_Screen_Driver::next_marked_length = (int)strlen([received UTF8String]); - if (!in_key_event) Fl::handle( FL_KEYBOARD, target); - else need_handle = YES; - selectedRange = NSMakeRange(100, newSelection.length); - fl_unlock_function(); -} - -- (void)unmarkText { - fl_lock_function(); - Fl_Cocoa_Screen_Driver::reset_marked_text(); - fl_unlock_function(); - //NSLog(@"unmarkText"); -} - -- (NSRange)selectedRange { - Fl_Widget *w = Fl::focus(); - if (w && w->use_accents_menu()) return selectedRange; - return NSMakeRange(NSNotFound, 0); -} - -- (NSRange)markedRange { - //NSLog(@"markedRange=%d %d", Fl::compose_state > 0?0:NSNotFound, Fl::compose_state); - return NSMakeRange(Fl::compose_state > 0?0:NSNotFound, Fl::compose_state); -} - -- (BOOL)hasMarkedText { - //NSLog(@"hasMarkedText %s", Fl::compose_state > 0?"YES":"NO"); - return (Fl::compose_state > 0); -} - -- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange { - return [self attributedSubstringForProposedRange:aRange actualRange:NULL]; -} -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - //NSLog(@"attributedSubstringFromRange: %d %d",aRange.location,aRange.length); - return nil; -} - -- (NSArray *)validAttributesForMarkedText { - return nil; -} - -- (NSRect)firstRectForCharacterRange:(NSRange)aRange { - return [self firstRectForCharacterRange:aRange actualRange:NULL]; -} -- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - //NSLog(@"firstRectForCharacterRange %d %d actualRange=%p",aRange.location, aRange.length,actualRange); - NSRect glyphRect; - fl_lock_function(); - Fl_Widget *focus = Fl::focus(); - Fl_Window *wfocus = [(FLWindow*)[self window] getFl_Window]; - if (!focus) focus = wfocus; - glyphRect.size.width = 0; - - int x, y, height; - if (Fl_Cocoa_Screen_Driver::insertion_point_location(&x, &y, &height)) { - glyphRect.origin.x = (CGFloat)x; - glyphRect.origin.y = (CGFloat)y; - } else { - if (focus->as_window()) { - glyphRect.origin.x = 0; - glyphRect.origin.y = focus->h(); - } - else { - glyphRect.origin.x = focus->x(); - glyphRect.origin.y = focus->y() + focus->h(); - } - height = 12; - } - glyphRect.size.height = height; - Fl_Window *win = focus->as_window(); - if (!win) win = focus->window(); - while (win != NULL && win != wfocus) { - glyphRect.origin.x += win->x(); - glyphRect.origin.y += win->y(); - win = win->window(); - } - // Convert the rect to screen coordinates - float s = Fl_Graphics_Driver::default_driver().scale(); - glyphRect.origin.x *= s; - glyphRect.origin.y *= s; - glyphRect.origin.y = wfocus->h()*s - glyphRect.origin.y; - glyphRect.origin = [(FLWindow*)[self window] convertBaseToScreen:glyphRect.origin]; - glyphRect.size.height *= s; - if (actualRange) *actualRange = aRange; - fl_unlock_function(); - return glyphRect; -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { - return 0; -} - -- (NSInteger)windowLevel { - return [[self window] level]; -} - -- (NSInteger)conversationIdentifier { - return identifier; -} - -- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context -{ - return NSDragOperationCopy; -} -- (void)draggingSession:(NSDraggingSession *)session - endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation -{ - Fl_Widget *w = Fl::pushed(); - if ( w ) { - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_RELEASE); - Fl::e_number = old_event; - Fl::pushed( 0 ); - } -} - -@end - - -/* - * Initialize the given port for redraw and call the window's flush() to actually draw the content - */ -void Fl_Cocoa_Window_Driver::flush() -{ - if (pWindow->as_gl_window()) { - Fl_Window_Driver::flush(); - } else { - through_Fl_X_flush = YES; - NSView *view = [fl_xid(pWindow) contentView]; - if (views_use_CA) [view display]; - else { - [view setNeedsDisplay:YES]; - [view displayIfNeededIgnoringOpacity]; - } - through_Fl_X_flush = NO; - } -} - -/* - * go ahead, create that (sub)window - */ -void Fl_Cocoa_Window_Driver::makeWindow() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - Fl_Group::current(0); - fl_open_display(); - NSInteger winlevel = NSNormalWindowLevel; - NSUInteger winstyle; - Fl_Sys_Menu_Bar::create_window_menu(); // effective once at most - Fl_Window* w = pWindow; - if (w->parent()) { - w->border(0); - show_iconic(0); - } - if (w->border()) { - winstyle = (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable); - if (is_resizable()) - winstyle |= NSWindowStyleMaskResizable; - } else { - winstyle = NSWindowStyleMaskBorderless; - } - if (show_iconic() && !w->parent()) { // prevent window from being out of work area when created iconized - int sx, sy, sw, sh; - Fl::screen_work_area (sx, sy, sw, sh, w->x(), w->y()); - if (w->x() < sx) x(sx); - if (w->y() < sy) y(sy); - } - int xp = w->x(); - int yp = w->y(); - - int xwm = xp, ywm = yp, bt, bx, by; - - if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) { - // menu windows and tooltips - if (w->modal()||w->tooltip_window()) { - winlevel = modal_window_level(); - } - } - if (w->modal()) { - winstyle &= ~NSWindowStyleMaskMiniaturizable; - winlevel = modal_window_level(); - } - else if (w->non_modal()) { - winlevel = non_modal_window_level(); - } - - if (force_position()) { - if (!Fl::grab()) { - xp = xwm; yp = ywm; - x(xp);y(yp); - } - xp -= bx; - yp -= by+bt; - } - - Fl_X *x = new Fl_X; - other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows - x->region = 0; - subRect(0); - gc = 0; - mapped_to_retina(false); - changed_resolution(false); - - NSRect crect; - float s = Fl::screen_driver()->scale(0); - crect.origin.x = round(s * w->x()); // correct origin set later for subwindows - crect.origin.y = main_screen_height - round(s * (w->y() + w->h())); - crect.size.width = int(s * w->w()); - crect.size.height = int(s * w->h()); - FLWindow *cw = [[FLWindow alloc] initWithFl_W:w - contentRect:crect - styleMask:winstyle]; - [cw setFrameOrigin:crect.origin]; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) { - if (!w->parent() && (winstyle & NSWindowStyleMaskTitled) && - (winstyle & NSWindowStyleMaskResizable) && !w->modal() && !w->non_modal() && - (Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() > Fl_Sys_Menu_Bar::tabbing_mode_none)) { - if (Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() == Fl_Sys_Menu_Bar::tabbing_mode_preferred) - [cw setTabbingMode:NSWindowTabbingModePreferred]; - else [cw setTabbingMode:NSWindowTabbingModeAutomatic]; - } else { - [cw setTabbingMode:NSWindowTabbingModeDisallowed]; - } - } -#endif - if (!w->parent()) { - [cw setHasShadow:YES]; - [cw setAcceptsMouseMovedEvents:YES]; - } - if (w->shape()) { - [cw setOpaque:NO]; // shaped windows must be non opaque - [cw setBackgroundColor:[NSColor clearColor]]; // and with transparent background color - } - x->xid = (fl_uintptr_t)cw; - x->w = w; - flx(x); - wait_for_expose_value = 1; - if (!w->parent()) { - x->next = Fl_X::first; - Fl_X::first = x; - } else if (Fl_X::first) { - x->next = Fl_X::first->next; - Fl_X::first->next = x; - } - else { - x->next = NULL; - Fl_X::first = x; - } - FLView *myview = [[FLView alloc] initWithFrame:crect]; - [cw setContentView:myview]; - [myview release]; - [cw setLevel:winlevel]; - - q_set_window_title(cw, w->label(), w->iconlabel()); - NSImage *icon = icon_image; // is a window or default icon present? - if (!icon) icon = ((Fl_Cocoa_Screen_Driver*)Fl::screen_driver())->default_icon; - if (icon && (winstyle & NSWindowStyleMaskTitled) && w->label() && strlen(w->label()) > 0) { - [cw setRepresentedFilename:[NSString stringWithFormat:@"/%@", [cw title]]]; - NSButton *icon_button = [cw standardWindowButton:NSWindowDocumentIconButton]; - if (icon_button) { - [icon setSize:[icon_button frame].size]; - [icon_button setImage:icon]; - } - } - if (!force_position()) { - if (w->modal()) { - [cw center]; - } else if (w->non_modal()) { - [cw center]; - } else if (!w->fullscreen_active()) { - static NSPoint delta = NSZeroPoint; - delta = [cw cascadeTopLeftFromPoint:delta]; - } - crect = [cw frame]; // synchronize FLTK's and the system's window coordinates - this->x(round(crect.origin.x/s)); - this->y( round((main_screen_height - crect.origin.y)/s) - w->h() ); - } - if(w->menu_window()) { // make menu windows slightly transparent - [cw setAlphaValue:0.97]; - } - // Install DnD handlers - [myview registerForDraggedTypes:[NSArray arrayWithObjects:UTF8_pasteboard_type, - fl_filenames_pboard_type, nil]]; - - if (pWindow->get_size_range(NULL, NULL, NULL, NULL, NULL, NULL, NULL)) size_range(); - - if ( w->border() || (!w->modal() && !w->tooltip_window()) ) { - Fl_Tooltip::enter(0); - } - - if (w->modal()) Fl::modal_ = w; - - w->set_visible(); - if ( w->border() || (!w->modal() && !w->tooltip_window() && - w->user_data() != (void*)&Fl_Screen_Driver::transient_scale_display) ) Fl::handle(FL_FOCUS, w); - [cw setDelegate:[FLWindowDelegate singleInstance]]; - if (show_iconic()) { - show_iconic(0); - w->handle(FL_SHOW); // create subwindows if any - if (fl_mac_os_version < 101300) { // TODO: threshold may be smaller - // draw the window and its subwindows before its icon is computed - [cw recursivelySendToSubwindows:@selector(display) applyToSelf:YES]; - } - [cw miniaturize:nil]; - } else if (w->parent()) { // a subwindow - [cw setIgnoresMouseEvents:YES]; // needs OS X 10.2 - // next 2 statements so a subwindow doesn't leak out of its parent window - [cw setOpaque:NO]; - [cw setBackgroundColor:[NSColor clearColor]]; // transparent background color - starting_moved_window = w; - [cw setSubwindowFrame]; - starting_moved_window = NULL; - // needed if top window was first displayed miniaturized - FLWindow *pxid = fl_xid(w->top_window()); - [pxid makeFirstResponder:[pxid contentView]]; - } else { // a top-level window - if ([cw canBecomeKeyWindow]) [cw makeKeyAndOrderFront:nil]; - else [cw orderFront:nil]; - if (w->fullscreen_active()) { - if (fullscreen_screen_top() >= 0) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) - cw.collectionBehavior |= NSWindowCollectionBehaviorFullScreenNone; -#endif - *no_fullscreen_x() = pWindow->x(); - *no_fullscreen_y() = pWindow->y(); - } - fullscreen_on(); - } - } - if (fl_sys_menu_bar && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style() && !w->parent() && w->border() && - !w->modal() && !w->non_modal()) { - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->new_window(w); - } - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_SHOW); - Fl::e_number = old_event; - - // if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); } - if (!w->parent()) [myview did_view_resolution_change]; // to set mapped_to_retina to its current state - [pool release]; -} - - -static BOOL fullscreen_screen_border = NO; // YES means the multi-screened window had a border before - - -static NSUInteger calc_win_style(Fl_Window *win); - - -void Fl_Cocoa_Window_Driver::fullscreen_on() { - pWindow->_set_fullscreen(); - bool has_border = pWindow->border(); - if (fullscreen_screen_top() >= 0 && has_border) { - fullscreen_screen_border = YES; - has_border = false; - } - if (has_border) { - NSWindow *nswin = fl_xid(pWindow); -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - if (fl_mac_os_version >= 101300) { - NSWindow *active_tab = [[nswin tabGroup] selectedWindow]; - if (active_tab) nswin = active_tab; - } -# endif - if (fullscreen_screen_border) { // from "All Screens" fullscreen to single-screen fullscreen - pWindow->_clear_fullscreen(); - [nswin setLevel:NSNormalWindowLevel]; - [nswin setStyleMask:calc_win_style(pWindow)]; //10.6 - pWindow->_set_fullscreen(); - } - [nswin toggleFullScreen:nil]; - } else { - FLWindow *nswin = fl_xid(pWindow); - if (nswin.styleMask & NSWindowStyleMaskFullScreen) { - // from single-screen fullscreen to "All Screens" fullscreen, with border -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) { - bool allscreens_on = (nswin.collectionBehavior & NSWindowCollectionBehaviorFullScreenNone); - if (allscreens_on) nswin.collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenNone; - [nswin toggleFullScreen:nil]; - if (allscreens_on) nswin.collectionBehavior |= NSWindowCollectionBehaviorFullScreenNone; - } else -#endif - [nswin toggleFullScreen:nil]; - if (*no_fullscreen_w() == 0) { - *no_fullscreen_x() = x(); - *no_fullscreen_y() = y(); - *no_fullscreen_w() = w(); - *no_fullscreen_h() = h(); - } - pWindow->_set_fullscreen(); - } - [nswin setStyleMask:NSWindowStyleMaskBorderless]; // 10.6 - if ([nswin isKeyWindow]) { - if ([nswin level] != NSStatusWindowLevel) { - [nswin setLevel:NSStatusWindowLevel]; - fixup_window_levels(); - } - } else if([nswin level] != NSNormalWindowLevel) { - [nswin setLevel:NSNormalWindowLevel]; - fixup_window_levels(); - } - int sx, sy, sw, sh, X, Y, W, H; - int top = fullscreen_screen_top(); - int bottom = fullscreen_screen_bottom(); - int left = fullscreen_screen_left(); - int right = fullscreen_screen_right(); - if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) { - top = Fl::screen_num(x(), y(), w(), h()); - bottom = top; - left = top; - right = top; - } - Fl::screen_xywh(sx, sy, sw, sh, top); - Y = sy; - Fl::screen_xywh(sx, sy, sw, sh, bottom); - H = sy + sh - Y; - Fl::screen_xywh(sx, sy, sw, sh, left); - X = sx; - Fl::screen_xywh(sx, sy, sw, sh, right); - W = sx + sw - X; - pWindow->resize(X, Y, W, H); - } - Fl::handle(FL_FULLSCREEN, pWindow); -} - - -void Fl_Cocoa_Window_Driver::maximize() { - if (border()) [fl_xid(pWindow) performZoom:nil]; - else Fl_Window_Driver::maximize(); -} - - -void Fl_Cocoa_Window_Driver::un_maximize() { - if (border()) [fl_xid(pWindow) performZoom:nil]; - else Fl_Window_Driver::un_maximize(); -} - - -static NSUInteger calc_win_style(Fl_Window *win) { - NSUInteger winstyle; - if (win->border() && !win->fullscreen_active()) { - winstyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; - if (Fl_Window_Driver::driver(win)->is_resizable()) winstyle |= NSWindowStyleMaskResizable; - if (!win->modal()) winstyle |= NSWindowStyleMaskMiniaturizable; - } else winstyle = NSWindowStyleMaskBorderless; - return winstyle; -} - -static void restore_window_title_and_icon(Fl_Window *pWindow, NSImage *icon) { - FLWindow *nswin = fl_xid(pWindow); - q_set_window_title(nswin, pWindow->label(), pWindow->iconlabel()); - if (!icon) icon = ((Fl_Cocoa_Screen_Driver*)Fl::screen_driver())->default_icon; - if (icon && ([nswin styleMask] & NSWindowStyleMaskTitled) && pWindow->label() && - (strlen(pWindow->label()) > 0)) { - NSButton *icon_button = [nswin standardWindowButton:NSWindowDocumentIconButton]; - if (icon_button) { - [icon setSize:[icon_button frame].size]; - [icon_button setImage:icon]; - } - } -} - - -void Fl_Cocoa_Window_Driver::fullscreen_off(int X, int Y, int W, int H) { - NSWindow *nswin = fl_xid(pWindow); - pWindow->_clear_fullscreen(); - if ([nswin styleMask] & NSWindowStyleMaskFullScreen) { -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 - if (fl_mac_os_version >= 101300) { - NSWindow *active_tab = [[nswin tabGroup] selectedWindow]; - if (active_tab) nswin = active_tab; - } -# endif - [nswin toggleFullScreen:nil]; - pWindow->resize(*no_fullscreen_x(), *no_fullscreen_y(), *no_fullscreen_w(), *no_fullscreen_h()); - } else { - // Transition from multi-screen fullscreen mode to normal mode - NSInteger level = NSNormalWindowLevel; - if (pWindow->modal()) level = modal_window_level(); - else if (pWindow->non_modal()) level = non_modal_window_level(); - /* Hide (orderOut) and later show (orderFront) the window to avoid a crash that - occurs in a very specific situation: the dock is at bottom and - H is larger than the maximum value for the display. - See "Crashing regression in MacOS code" in fltk.coredev. - */ - BOOL has_focus = [nswin isKeyWindow]; - [nswin orderOut:nil]; - [nswin setLevel:level]; - [nswin setStyleMask:calc_win_style(pWindow)]; //10.6 - restore_window_title_and_icon(pWindow, icon_image); - pWindow->resize(X, Y, W, H); - if (pWindow->maximize_active()) Fl_Window_Driver::maximize(); - if (has_focus) [nswin makeKeyAndOrderFront:nil]; - else [nswin orderFront:nil]; - } - Fl::handle(FL_FULLSCREEN, pWindow); - fullscreen_screen_border = NO; -} - - -void Fl_Cocoa_Window_Driver::fullscreen_screens(bool on_off) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 - if (fl_mac_os_version >= 101200) { - FLWindow *xid = fl_mac_xid(pWindow); - if (on_off) xid.collectionBehavior |= NSWindowCollectionBehaviorFullScreenNone; - else xid.collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenNone; - } -#endif -} - - -void Fl_Cocoa_Window_Driver::use_border() { - if (!shown() || pWindow->parent()) return; - if (pWindow->fullscreen_active() || pWindow->maximize_active()) { - // prevent changing border while window is fullscreen or maximized - static bool active = false; - if (!active) { - active = true; - bool b = !border(); - pWindow->border(b); - active = false; - } - return; - } - [fl_xid(pWindow) setStyleMask:calc_win_style(pWindow)]; // 10.6 - if (border()) restore_window_title_and_icon(pWindow, icon_image); - pWindow->redraw(); -} - -/* - * Tell the OS what window sizes we want to allow - */ -void Fl_Cocoa_Window_Driver::size_range() { - Fl_X *i = Fl_X::flx(pWindow); - if (i && i->xid) { - float s = Fl::screen_driver()->scale(0); - int bt = get_window_frame_sizes(pWindow); - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - NSSize minSize = NSMakeSize(int(minw * s +.5) , int(minh * s +.5) + bt); - NSSize maxSize = NSMakeSize(maxw ? int(maxw * s + .5):32000, maxh ? int(maxh * s +.5) + bt:32000); - [(FLWindow*)i->xid setMinSize:minSize]; - [(FLWindow*)i->xid setMaxSize:maxSize]; - } -} - -void Fl_Cocoa_Window_Driver::wait_for_expose() -{ - if (fl_mac_os_version < 101300) { - [fl_xid(pWindow) recursivelySendToSubwindows:@selector(waitForExpose) applyToSelf:YES]; - } else { - Fl_Window_Driver::wait_for_expose(); - } -} - -/* - * set the window title bar name - */ -void Fl_Cocoa_Window_Driver::label(const char *name, const char *mininame) { - if (shown() || Fl_X::flx(pWindow)) { - q_set_window_title(fl_xid(pWindow), name, mininame); - if (fl_sys_menu_bar && Fl_Sys_Menu_Bar_Driver::window_menu_style()) - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->rename_window(pWindow); - } -} - - -/* - * make a window visible - */ -void Fl_Cocoa_Window_Driver::show() { - Fl_X *top = NULL; - if (parent()) top = Fl_X::flx(pWindow->top_window()); - if (!shown() && (!parent() || (top && ![(FLWindow*)top->xid isMiniaturized]))) { - makeWindow(); - } else { - if ( !parent() ) { - Fl_X *i = Fl_X::flx(pWindow); - if ([(FLWindow*)i->xid isMiniaturized]) { - i->w->redraw(); - [(FLWindow*)i->xid deminiaturize:nil]; - } - if (!fl_capture) { - [(FLWindow*)i->xid makeKeyAndOrderFront:nil]; - } - } - else pWindow->set_visible(); - } -} - -/* - * resize a window - */ -void Fl_Cocoa_Window_Driver::resize(int X, int Y, int W, int H) { - if (!pWindow->shown() && (X != x() || Y != y())) force_position(1); - if (view_resized() || !visible_r()) { - pWindow->Fl_Group::resize(X, Y, W, H); - if (!pWindow->shown()) pWindow->init_sizes(); - } else if (!through_resize()) { - NSPoint pt = FLTKtoCocoa(pWindow, X, Y, H); - FLWindow *xid = fl_xid(pWindow); - through_resize(1); - if (W != w() || H != h() || Fl_Window::is_a_rescale()) { - NSRect r; - float s = Fl::screen_driver()->scale(screen_num()); - int bt = get_window_frame_sizes(pWindow); - r.origin = pt; - r.size.width = round(W*s); - r.size.height = round(H*s) + bt; - if (NSEqualRects(r, [xid frame])) { - pWindow->Fl_Group::resize(X, Y, W, H); // runs rarely, e.g. with scaled down test/tabs - pWindow->redraw(); - } else { - // First resize the logical FLTK coordinates for this and all children - if (!Fl_Window::is_a_rescale()) - pWindow->Fl_Group::resize(X, Y, W, H); - // Next update the physical Cocoa view - [xid setFrame:r display:YES]; - [[xid contentView] displayIfNeededIgnoringOpacity]; - // Finally tell the the group to render its contents if the code above - // didn't already - pWindow->redraw(); - } - } - else { - if (pWindow->parent()) starting_moved_window = pWindow; - if (!NSEqualPoints([xid frame].origin, pt)) - [xid setFrameOrigin:pt]; // set cocoa coords to FLTK position - else { - x(X); y(Y); - } - if (pWindow->parent()) starting_moved_window = NULL; - } - through_resize(0); - } - - // make sure subwindow doesn't leak outside parent - if (pWindow->parent()) [fl_xid(pWindow) checkSubwindowFrame]; -} - - -/* - * make all drawing go into this window (called by subclass flush() impl.) - - This can be called in 3 different situations: - - 1) When a window is created, resized or moved between low/high resolution displays. - macOS sends the drawRect: message to the window view after having prepared the - current graphics context to draw to this view. The drawRect: method sets through_drawRect - to YES and calls Fl_Window_Driver::flush(). Fl_Window_Driver::flush() calls - Fl_Window::flush() that calls Fl_Window::make_current() that uses the graphics - context of the window or the layer. The window's draw() function is then executed. - - 2) At each round of the FLTK event loop. - Fl::flush() is called, that calls Fl_Cocoa_Window_Driver::flush() on each window that needs drawing. - Fl_Cocoa_Window_Driver::flush() sets through_Fl_X_Flush to YES and marks the view as - needing display. The view is sent the displayIfNeededIgnoringOpacity or display message which makes - the OS send the view the drawRect: message. The program proceeds next as in 1) above. - - 3) An FLTK application can call Fl_Window::make_current() at any time before it draws to a window. - This occurs for instance in the idle callback function of the mandelbrot test program. Variables - through_Fl_X_flush and through_drawRect equal NO. - Before 10.14: The window graphics context is obtained. Subsequent drawing requests go to the window. - After 10.14: The layered view is marked as needing display. It will be sent the drawRect: message - at the next event loop. Subsequent drawing operations, until drawRect: runs, are sent to view->aux_bitmap. - - CAUTION: it's not possible to call Fl::wait(), Fl::check() nor Fl::ready() while in the draw() - function of a widget. Use an idle callback instead. - */ -void Fl_Cocoa_Window_Driver::make_current() -{ - q_release_context(); - Fl_X *i = Fl_X::flx(pWindow); - //NSLog(@"region-count=%d damage=%u",i->region?i->region->count:0, pWindow->damage()); - fl_window = (FLWindow*)i->xid; - ((Fl_Quartz_Graphics_Driver&)Fl_Graphics_Driver::default_driver()).high_resolution( mapped_to_retina() ); - - if (pWindow->as_overlay_window() && other_xid && changed_resolution()) { - destroy_double_buffer(); - changed_resolution(false); - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - FLView *view = (FLView*)[fl_window contentView]; - if (views_use_CA && !through_drawRect) { // detect direct calls from the app - [view setNeedsDisplay:YES]; - } - if (views_use_CA && view->aux_bitmap) { - gc = view->aux_bitmap; - } else -#endif - { -// ignore deprecation warning of "graphicsContextWithWindow" because used only with 10.13 or before -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSGraphicsContext *nsgc = (through_drawRect ? [NSGraphicsContext currentContext] : - [NSGraphicsContext graphicsContextWithWindow:fl_window]); -#pragma clang diagnostic pop - static SEL gc_sel = fl_mac_os_version >= 101000 ? @selector(CGContext) : @selector(graphicsPort); - gc = (CGContextRef)[nsgc performSelector:gc_sel]; - } - Fl_Graphics_Driver::default_driver().gc(gc); -#if defined(FLTK_HAVE_CAIROEXT) - CGContextSaveGState(gc); // one extra level -#endif - CGContextSaveGState(gc); // native context - // antialiasing must be deactivated because it applies to rectangles too - // and escapes even clipping!!! - // it gets activated when needed (e.g., draw text) - CGContextSetShouldAntialias(gc, false); - CGFloat hgt = [[fl_window contentView] frame].size.height; - float s = Fl::screen_driver()->scale(0); - CGContextTranslateCTM(gc, 0.5f*s, hgt-0.5f*s); - CGContextScaleCTM(gc, 1.0f, -1.0f); // now 0,0 is top-left point of the window - CGContextScaleCTM(gc, s, s); // apply current scaling factor - // for subwindows, limit drawing to inside of parent window - // half pixel offset is necessary for clipping as done by fl_cgrectmake_cocoa() - if (subRect()) { - CGContextClipToRect(gc, CGRectOffset(*(subRect()), -0.5, -0.5)); - } -// this is the context with origin at top left of (sub)window - CGContextSaveGState(gc); - fl_clip_region( 0 ); -#ifdef FLTK_HAVE_CAIROEXT - // update the cairo_t context - if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow); -#endif -} - -// Give the Quartz context back to the system -void Fl_Cocoa_Window_Driver::q_release_context(Fl_Cocoa_Window_Driver *x) { - CGContextRef gc = (CGContextRef)Fl_Graphics_Driver::default_driver().gc(); - if (x && x->shown() && x->gc != gc) return; - if (!gc) return; - CGContextRestoreGState(gc); // match the CGContextSaveGState's of make_current - CGContextRestoreGState(gc); - CGContextFlush(gc); - Fl_Graphics_Driver::default_driver().gc(0); -#if defined(FLTK_HAVE_CAIROEXT) - CGContextRestoreGState(gc); -#endif -} - - -static NSBitmapImageRep *pdf_to_nsbitmapimagerep(NSData *pdfdata) { - NSImage *image = [[NSImage alloc] initWithData:pdfdata]; - NSInteger width = [image size].width * 2; - NSInteger height = [image size].height * 2; - NSBitmapImageRep *bitmap = [NSBitmapImageRep alloc]; - NSRect dest_r = NSMakeRect(0, 0, width, height); -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 - if (fl_mac_os_version >= 100900) { - // This procedure is necessary because initWithFocusedViewRect is deprecated in macOS 10.14 - // and because it produces a bitmap with floating point pixel values with macOS 11.x - bitmap = [bitmap initWithBitmapDataPlanes:NULL - pixelsWide:width - pixelsHigh:height - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:0 - bitsPerPixel:0]; - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - [NSGraphicsContext saveGraphicsState]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];// 10.4 - [[NSColor clearColor] set]; - NSRect r = NSMakeRect(0, 0, width, height); - NSRectFill(r); - [image drawInRect:dest_r]; // 10.9 - [NSGraphicsContext restoreGraphicsState]; - [localPool release]; - } else -#endif - { - [image lockFocus]; - // the deprecation warning at 10.14 can be ignored because runs only for macOS < 10.9 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - bitmap = [bitmap initWithFocusedViewRect:dest_r]; -#pragma clang diagnostic pop - [image unlockFocus]; - } - [bitmap setSize:[image size]]; - [image release]; - return bitmap; -} - - -Fl_Quartz_Copy_Surface_Driver::~Fl_Quartz_Copy_Surface_Driver() -{ - CGContextRestoreGState(gc); - CGContextEndPage(gc); - CGPDFContextClose(gc); // needs 10.5, necessary with macOS 10.15 - CGContextRelease(gc); - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - [clip declareTypes:[NSArray arrayWithObjects:PDF_pasteboard_type, TIFF_pasteboard_type, nil] owner:nil]; - [clip setData:(NSData*)pdfdata forType:PDF_pasteboard_type]; - - //second, transform this PDF to a bitmap image and put it as tiff in clipboard with retina resolution - NSBitmapImageRep *bitmap = pdf_to_nsbitmapimagerep((NSData*)pdfdata); - CFRelease(pdfdata); - [clip setData:[bitmap TIFFRepresentation] forType:TIFF_pasteboard_type]; - [bitmap release]; - delete driver(); -} - -//////////////////////////////////////////////////////////////// -// Copy & Paste fltk implementation. -//////////////////////////////////////////////////////////////// - -// clipboard variables definitions : -char *fl_selection_buffer[2] = {NULL, NULL}; -int fl_selection_length[2] = {0, 0}; -static int fl_selection_buffer_length[2]; - -extern void fl_trigger_clipboard_notify(int source); - -static void clipboard_check(void) -{ - static NSInteger oldcount = -1; - NSInteger newcount = [[NSPasteboard generalPasteboard] changeCount]; - if (newcount == oldcount) return; - oldcount = newcount; - fl_trigger_clipboard_notify(1); -} - -static void resize_selection_buffer(int len, int clipboard) { - if (len <= fl_selection_buffer_length[clipboard]) - return; - delete[] fl_selection_buffer[clipboard]; - fl_selection_buffer[clipboard] = new char[len+100]; - fl_selection_buffer_length[clipboard] = len+100; -} - -/* - * create a selection - * stuff: pointer to selected data - * len: size of selected data - * type: always "plain/text" for now - */ -void Fl_Cocoa_Screen_Driver::copy(const char *stuff, int len, int clipboard, const char *type) { - if (!stuff || len<0) return; - if (clipboard >= 2) - clipboard = 1; // Only on X11 do multiple clipboards make sense. - - resize_selection_buffer(len+1, clipboard); - memcpy(fl_selection_buffer[clipboard], stuff, len); - fl_selection_buffer[clipboard][len] = 0; // needed for direct paste - fl_selection_length[clipboard] = len; - if (clipboard) { - CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len); - if (text==NULL) return; // there was a pb creating the object, abort. - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - [clip declareTypes:[NSArray arrayWithObject:UTF8_pasteboard_type] owner:nil]; - [clip setData:(NSData*)text forType:UTF8_pasteboard_type]; - CFRelease(text); - } -} - -static int get_plain_text_from_clipboard(int clipboard) -{ - NSInteger length = 0; - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - NSString *found = [clip availableTypeFromArray:[NSArray arrayWithObjects:UTF8_pasteboard_type, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]]; - if (found) { - NSData *data = [clip dataForType:found]; - if (data) { - NSInteger len; - char *aux_c = NULL; - if (![found isEqualToString:UTF8_pasteboard_type]) { - NSString *auxstring; - auxstring = (NSString *)CFStringCreateWithBytes(NULL, (const UInt8*)[data bytes], - [data length], - ([found isEqualToString:@"public.utf16-plain-text"] ? - kCFStringEncodingUnicode : kCFStringEncodingMacRoman), false); - aux_c = fl_strdup([auxstring UTF8String]); - [auxstring release]; - len = strlen(aux_c) + 1; - } - else len = [data length] + 1; - resize_selection_buffer((int)len, clipboard); - if (![found isEqualToString:UTF8_pasteboard_type]) { - strcpy(fl_selection_buffer[clipboard], aux_c); - free(aux_c); - } - else { - [data getBytes:fl_selection_buffer[clipboard] length:[data length]]; - } - fl_selection_buffer[clipboard][len - 1] = 0; - length = Fl_Screen_Driver::convert_crlf(fl_selection_buffer[clipboard], len - 1); // turn all \r characters into \n: - Fl::e_clipboard_type = Fl::clipboard_plain_text; - } - } - return (int)length; -} - -static Fl_RGB_Image* get_image_from_clipboard(Fl_Widget *receiver) -{ - NSPasteboard *clip = [NSPasteboard generalPasteboard]; - NSArray *present = [clip types]; // types in pasteboard in order of decreasing preference - NSArray *possible = [NSArray arrayWithObjects:PDF_pasteboard_type, TIFF_pasteboard_type, PICT_pasteboard_type, nil]; - NSString *found = nil; - NSUInteger rank; - for (NSUInteger i = 0; (!found) && i < [possible count]; i++) { - for (rank = 0; rank < [present count]; rank++) { // find first of possible types present in pasteboard - if ([[present objectAtIndex:rank] isEqualToString:[possible objectAtIndex:i]]) { - found = [present objectAtIndex:rank]; - break; - } - } - } - if (!found) return NULL; - NSData *data = [clip dataForType:found]; - if (!data) return NULL; - NSBitmapImageRep *bitmap = nil; - if ([found isEqualToString:TIFF_pasteboard_type]) { - bitmap = [[NSBitmapImageRep alloc] initWithData:data]; - } - else if ([found isEqualToString:PDF_pasteboard_type] || [found isEqualToString:PICT_pasteboard_type]) { - bitmap = pdf_to_nsbitmapimagerep(data); - } - if (!bitmap) return NULL; - int bytesPerPixel((int)[bitmap bitsPerPixel]/8); - int bpr((int)[bitmap bytesPerRow]); - int hh((int)[bitmap pixelsHigh]); - int ww((int)[bitmap pixelsWide]); - uchar *imagedata = new uchar[bpr * hh]; - memcpy(imagedata, [bitmap bitmapData], bpr * hh); - Fl_RGB_Image *image = new Fl_RGB_Image(imagedata, ww, hh, bytesPerPixel, (bpr == ww * bytesPerPixel ? 0 : bpr) ); - image->scale([bitmap size].width, [bitmap size].height); - image->alloc_array = 1; - [bitmap release]; - Fl::e_clipboard_type = Fl::clipboard_image; - return image; -} - -// Call this when a "paste" operation happens: -void Fl_Cocoa_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) { - if (type[0] == 0) type = Fl::clipboard_plain_text; - if (clipboard) { - Fl::e_clipboard_type = ""; - if (strcmp(type, Fl::clipboard_plain_text) == 0) { - fl_selection_length[1] = get_plain_text_from_clipboard(1); - } - else if (strcmp(type, Fl::clipboard_image) == 0) { - Fl::e_clipboard_data = get_image_from_clipboard(&receiver); - if (Fl::e_clipboard_data) { - int done = receiver.handle(FL_PASTE); - Fl::e_clipboard_type = ""; - if (done == 0) { - delete (Fl_Image*)Fl::e_clipboard_data; - Fl::e_clipboard_data = NULL; - } - } - return; - } - else - fl_selection_length[1] = 0; - } - Fl::e_text = fl_selection_buffer[clipboard]; - Fl::e_length = fl_selection_length[clipboard]; - if (!Fl::e_length) Fl::e_text = (char *)""; - receiver.handle(FL_PASTE); -} - -int Fl_Cocoa_Screen_Driver::clipboard_contains(const char *type) { - NSString *found = nil; - if (strcmp(type, Fl::clipboard_plain_text) == 0) { - found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:UTF8_pasteboard_type, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]]; - } - else if (strcmp(type, Fl::clipboard_image) == 0) { - found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:TIFF_pasteboard_type, PDF_pasteboard_type, PICT_pasteboard_type, nil]]; - } - return found != nil; -} - -void Fl_Cocoa_Window_Driver::destroy(FLWindow *xid) { - [[xid parentWindow] removeChildWindow:xid]; // necessary until 10.6 at least - if (fl_sys_menu_bar && Fl_Sys_Menu_Bar_Driver::window_menu_style()) - Fl_MacOS_Sys_Menu_Bar_Driver::driver()->remove_window([xid getFl_Window]); - [xid close]; -} - - -void Fl_Cocoa_Window_Driver::map() { - FLWindow *xid = fl_xid(pWindow); - if (pWindow && xid && ![xid parentWindow]) { // 10.2 - // after a subwindow has been unmapped, it has lost its parent window and its frame may be wrong - [xid setSubwindowFrame]; - } - if (cursor) { - [cursor release]; - cursor = NULL; - } -} - - -void Fl_Cocoa_Window_Driver::unmap() { - FLWindow *xid = fl_xid(pWindow); - if (pWindow && xid) { - if (parent()) [[xid parentWindow] removeChildWindow:xid]; // necessary with at least 10.5 - [xid orderOut:nil]; - } -} - - -void Fl_Cocoa_Window_Driver::iconize() { - [fl_xid(pWindow) miniaturize:nil]; -} - -static NSImage *CGBitmapContextToNSImage(CGContextRef c) -// the returned NSImage is autoreleased -{ - CGImageRef cgimg = CGBitmapContextCreateImage(c); // requires 10.4 - NSImage* image = [[NSImage alloc] initWithCGImage:cgimg size:NSZeroSize]; // requires 10.6 - CFRelease(cgimg); - return [image autorelease]; -} - -int Fl_Cocoa_Window_Driver::set_cursor(Fl_Cursor c) -{ - if (cursor) { - [(NSCursor*)cursor release]; - cursor = NULL; - } - - switch (c) { - case FL_CURSOR_ARROW: cursor = [NSCursor arrowCursor]; break; - case FL_CURSOR_CROSS: cursor = [NSCursor crosshairCursor]; break; - case FL_CURSOR_INSERT: cursor = [NSCursor IBeamCursor]; break; - case FL_CURSOR_HAND: cursor = [NSCursor pointingHandCursor]; break; - case FL_CURSOR_MOVE: cursor = [NSCursor openHandCursor]; break; - case FL_CURSOR_NS: cursor = [NSCursor resizeUpDownCursor]; break; - case FL_CURSOR_WE: cursor = [NSCursor resizeLeftRightCursor]; break; - case FL_CURSOR_N: cursor = [NSCursor resizeUpCursor]; break; - case FL_CURSOR_E: cursor = [NSCursor resizeRightCursor]; break; - case FL_CURSOR_W: cursor = [NSCursor resizeLeftCursor]; break; - case FL_CURSOR_S: cursor = [NSCursor resizeDownCursor]; break; - default: - return 0; - } - - [(NSCursor*)cursor retain]; - - [fl_xid(pWindow) invalidateCursorRectsForView:[fl_xid(pWindow) contentView]]; - - return 1; -} - -int Fl_Cocoa_Window_Driver::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { - if (cursor) { - [(NSCursor*)cursor release]; - cursor = NULL; - } - - if ((hotx < 0) || (hotx >= image->w())) - return 0; - if ((hoty < 0) || (hoty >= image->h())) - return 0; - - if (image->as_svg_image()) { - Fl_RGB_Image *image2 = (Fl_RGB_Image*)image->copy(); - image2->normalize(); - image = image2; - } - // OS X >= 10.6 can create a NSImage from a CGImage, but we need to - // support older versions, hence this pesky handling. - - NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:NULL - pixelsWide:image->data_w() - pixelsHigh:image->data_h() - bitsPerSample:8 - samplesPerPixel:image->d() - hasAlpha:!(image->d() & 1) - isPlanar:NO - colorSpaceName:(image->d() <= 2 ? - NSDeviceWhiteColorSpace : NSDeviceRGBColorSpace) - bytesPerRow:(image->data_w() * image->d()) - bitsPerPixel:(image->d()*8)]; - - // Alpha needs to be premultiplied for this format - - const uchar *i = (const uchar*)*image->data(); - const int extra_data = image->ld() ? (image->ld() - image->data_w() * image->d()) : 0; - unsigned char *o = [bitmap bitmapData]; - for (int y = 0;y < image->data_h();y++) { - if (!(image->d() & 1)) { - for (int x = 0;x < image->data_w();x++) { - unsigned int alpha; - if (image->d() == 4) { - alpha = i[3]; - *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); - *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); - } - - alpha = i[1]; - *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); - *o++ = alpha; - i++; - } - } else { - // No alpha, so we can just copy everything directly. - int len = image->data_w() * image->d(); - memcpy(o, i, len); - o += len; - i += len; - } - i += extra_data; - } - - NSImage *nsimage = [[NSImage alloc] - initWithSize:NSMakeSize(image->w(), image->h())]; - - [nsimage addRepresentation:bitmap]; - - cursor = [[NSCursor alloc] - initWithImage:nsimage - hotSpot:NSMakePoint(hotx, hoty)]; - - [fl_xid(pWindow) invalidateCursorRectsForView:[fl_xid(pWindow) contentView]]; - - [bitmap release]; - [nsimage release]; - if (image->as_svg_image()) delete image; - - return 1; -} - -@interface PrintWithTitlebarItem : NSMenuItem { -} -- (void) toggleCallback; -@end - -@implementation PrintWithTitlebarItem -- (void) toggleCallback { - NSMenuItem *item = [self representedObject]; - const char *title; - if ([self state] == NSControlStateValueOn) { - [self setState:NSControlStateValueOff]; - title = Fl_Mac_App_Menu::print_no_titlebar; - } else { - [self setState:NSControlStateValueOn]; - title = Fl_Mac_App_Menu::print; - } - [item setTitle:NSLocalizedString([NSString stringWithUTF8String:title], nil)]; -} -@end - -static PrintWithTitlebarItem *print_with_titlebar_item = NULL; - -@interface FLaboutItemTarget : NSObject -{ -} -- (BOOL)validateMenuItem:(NSMenuItem *)item; -- (void)showPanel; -- (void)printPanel; -- (void)terminate:(id)sender; -@end -@implementation FLaboutItemTarget -- (BOOL)validateMenuItem:(NSMenuItem *)item -{ // invalidate the Quit item of the application menu when running modal or when in native file chooser - if ([[NSApp keyWindow] isKindOfClass:[NSSavePanel class]]) return NO; - if (!Fl::modal() || [item action] != @selector(terminate:)) return YES; - return NO; -} -- (void)showPanel -{ - NSDictionary *options; - options = [NSDictionary dictionaryWithObjectsAndKeys: - [[[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@" GUI with FLTK %d.%d", - FL_MAJOR_VERSION, FL_MINOR_VERSION ]] autorelease], @"Credits", - nil]; - [NSApp orderFrontStandardAboutPanelWithOptions:options]; -} -- (void)printPanel -{ - bool grab_decoration = ([print_with_titlebar_item state] == NSControlStateValueOn); - fl_lock_function(); - fl_print_or_copy_window(Fl::first_window(), grab_decoration, 1); - fl_unlock_function(); -} -- (void)terminate:(id)sender -{ - [NSApp terminate:sender]; -} -@end - -static void createAppleMenu(void) -{ - static BOOL donethat = NO; - if (donethat) return; - donethat = YES; - NSMenu *mainmenu, *services = nil, *appleMenu; - NSMenuItem *menuItem; - NSString *title; - - SEL infodictSEL = (fl_mac_os_version >= 100200 ? @selector(localizedInfoDictionary) : @selector(infoDictionary)); - NSString *nsappname = [[[NSBundle mainBundle] performSelector:infodictSEL] objectForKey:@"CFBundleName"]; - if (nsappname == nil) - nsappname = [[NSProcessInfo processInfo] processName]; - appleMenu = [[NSMenu alloc] initWithTitle:@""]; - /* Add menu items */ - title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::about],nil), nsappname]; - menuItem = [appleMenu addItemWithTitle:title action:@selector(showPanel) keyEquivalent:@""]; - FLaboutItemTarget *about = [[FLaboutItemTarget alloc] init]; - [menuItem setTarget:about]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - // Print front window - title = NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::print], nil); - if ([title length] > 0) { - menuItem = [appleMenu - addItemWithTitle:title - action:@selector(printPanel) - keyEquivalent:@""]; - [menuItem setTarget:about]; - [menuItem setEnabled:YES]; - // Toggle "Print Window with titlebar" / "Print Window" - title = NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::toggle_print_titlebar], nil); - print_with_titlebar_item = [[PrintWithTitlebarItem alloc] initWithTitle:title - action:@selector(toggleCallback) - keyEquivalent:@""]; - [appleMenu addItem:print_with_titlebar_item]; - [print_with_titlebar_item setTarget:print_with_titlebar_item]; - [print_with_titlebar_item setRepresentedObject:menuItem]; - [print_with_titlebar_item setState:NSControlStateValueOn]; - [print_with_titlebar_item setEnabled:YES]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - } - // Services Menu - services = [[NSMenu alloc] initWithTitle:@""]; - menuItem = [appleMenu - addItemWithTitle:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::services], nil) - action:nil - keyEquivalent:@""]; - [appleMenu setSubmenu:services forItem:menuItem]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - // Hide AppName - title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::hide],nil), nsappname]; - [appleMenu addItemWithTitle:title - action:@selector(hide:) - keyEquivalent:@"h"]; - // Hide Others - menuItem = [appleMenu - addItemWithTitle:NSLocalizedString( - [NSString stringWithUTF8String:Fl_Mac_App_Menu::hide_others] , nil) - action:@selector(hideOtherApplications:) - keyEquivalent:@"h"]; - [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; - // Show All - [appleMenu addItemWithTitle:NSLocalizedString( - [NSString stringWithUTF8String:Fl_Mac_App_Menu::show], nil) - action:@selector(unhideAllApplications:) - keyEquivalent:@""]; - [appleMenu addItem:[NSMenuItem separatorItem]]; - // Quit AppName - title = [NSString stringWithFormat:NSLocalizedString( - [NSString stringWithUTF8String:Fl_Mac_App_Menu::quit], nil), - nsappname]; - menuItem = [appleMenu addItemWithTitle:title - action:@selector(terminate:) - keyEquivalent:@"q"]; - [menuItem setTarget:about]; - - /* Put menu into the menubar */ - menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; - [menuItem setSubmenu:appleMenu]; - mainmenu = [[NSMenu alloc] initWithTitle:@""]; - [mainmenu addItem:menuItem]; - [NSApp setMainMenu:mainmenu]; - if (services) { - [NSApp setServicesMenu:services]; - [services release]; - } - [mainmenu release]; - [appleMenu release]; - [menuItem release]; - Fl_MacOS_Sys_Menu_Bar_Driver::driver(); -} - - -void Fl_Cocoa_Window_Driver::set_key_window() -{ - [fl_xid(pWindow) makeKeyWindow]; -} - -static NSImage *imageFromText(const char *text, int *pwidth, int *pheight) -{ - const char *p, *q; - int width = 0, height, w2, ltext = (int)strlen(text); - fl_font(FL_HELVETICA, 10); - p = text; - int nl = 0; - while(nl < 100 && (q=strchr(p, '\n')) != NULL) { - nl++; - w2 = (int)fl_width(p, (int)(q - p)); - if (w2 > width) width = w2; - p = q + 1; - } - if (text[ ltext - 1] != '\n') { - nl++; - w2 = int(fl_width(p)); - if (w2 > width) width = w2; - } - height = nl * fl_height() + 3; - width += 6; - Fl_Image_Surface *off = new Fl_Image_Surface(width, height, 1); - Fl_Surface_Device::push_current(off); - CGContextSetRGBFillColor( (CGContextRef)off->offscreen(), 0,0,0,0); - fl_rectf(0,0,width,height); - fl_color(FL_BLACK); - p = text; - fl_font(FL_HELVETICA, 10); - int y = fl_height(); - while(TRUE) { - q = strchr(p, '\n'); - if (q) { - fl_draw(p, (int)(q - p), 3, y); - } else { - fl_draw(p, 3, y); - break; - } - y += fl_height(); - p = q + 1; - } - Fl_Surface_Device::pop_current(); - NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off->offscreen() ); - delete off; - *pwidth = width; - *pheight = height; - return image; -} - -static NSImage *defaultDragImage(int *pwidth, int *pheight) -{ - const int width = 50, height = 40; - Fl_Image_Surface *off = new Fl_Image_Surface(width, height, 1); - Fl_Surface_Device::push_current(off); - fl_font(FL_HELVETICA, 20); - fl_color(FL_BLACK); - char str[4]; - // the "Delivery truck" Unicode character from "Apple Color Emoji" font - int l = fl_utf8encode(0x1F69A, str); - fl_draw(str, l, 1, 16); - Fl_Surface_Device::pop_current(); - NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off->offscreen() ); - delete off; - *pwidth = width; - *pheight = height; - return image; -} - - -int Fl_Cocoa_Screen_Driver::dnd(int use_selection) -{ - CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[0], fl_selection_length[0]); - if (text==NULL) return false; - NSAutoreleasePool *localPool; - localPool = [[NSAutoreleasePool alloc] init]; - Fl_Widget *w = Fl::pushed(); - Fl_Window *win = w->top_window(); - FLView *myview = (FLView*)[fl_mac_xid(win) contentView]; - NSEvent *theEvent = [NSApp currentEvent]; - - int width, height; - NSImage *image; - if (use_selection) { - fl_selection_buffer[0][ fl_selection_length[0] ] = 0; - image = imageFromText(fl_selection_buffer[0], &width, &height); - } else { - image = defaultDragImage(&width, &height); - } - - NSPoint pt = [theEvent locationInWindow]; - pt.x -= width/2; - pt.y -= height/2; - NSPasteboardItem *pbItem = [[[NSPasteboardItem alloc] init] autorelease]; - [pbItem setData:(NSData*)text forType:UTF8_pasteboard_type]; - NSDraggingItem *dragItem = [[[NSDraggingItem alloc] initWithPasteboardWriter:pbItem] autorelease]; - NSRect r = {pt, {CGFloat(width), CGFloat(height)}}; - [dragItem setDraggingFrame:r contents:image]; - [myview beginDraggingSessionWithItems:[NSArray arrayWithObject:dragItem] event:theEvent source:myview]; - CFRelease(text); - [localPool release]; - return true; -} - -// rescales an NSBitmapImageRep (and also rewrites it with integer pixels) -static NSBitmapImageRep *scale_nsbitmapimagerep(NSBitmapImageRep *img, float scale) -{ - int w = (int)[img pixelsWide]; - int h = (int)[img pixelsHigh]; - long int scaled_w = lround(scale * w); - long int scaled_h = lround(scale * h); - NSBitmapImageRep *scaled = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL - pixelsWide:scaled_w - pixelsHigh:scaled_h - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:scaled_w*4 - bitsPerPixel:32]; - NSDictionary *dict = - [NSDictionary dictionaryWithObject:scaled - forKey:NSGraphicsContextDestinationAttributeName]; - NSGraphicsContext *oldgc = [NSGraphicsContext currentContext]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithAttributes:dict]]; - [[NSColor clearColor] set]; - NSRect r = NSMakeRect(0, 0, scaled_w, scaled_h); - NSRectFill(r); - [img drawInRect:r]; - [NSGraphicsContext setCurrentContext:oldgc]; - [img release]; - return scaled; -} - -static void write_bitmap_inside(NSBitmapImageRep *to, int to_width, NSBitmapImageRep *from, - int to_x, int to_y) - /* Copies in bitmap "to" the bitmap "from" with its top-left angle at coordinates to_x, to_y - On retina displays both bitmaps have double width and height - to_width is the width in screen units of "to". On retina, its pixel width is twice that. - */ -{ - const uchar *from_data = [from bitmapData]; - // 10.4 required by the bitmapFormat message - if (([to bitmapFormat] & NSBitmapFormatAlphaFirst) && - !([from bitmapFormat] & NSBitmapFormatAlphaFirst) ) { - // "to" is ARGB and "from" is RGBA --> convert "from" to ARGB - // it is enough to read "from" starting one byte earlier, because A is always 0xFF: - // RGBARGBA becomes (A)RGBARGB - from_data--; - } else if ( !([to bitmapFormat] & NSBitmapFormatAlphaFirst) && ([from bitmapFormat] & NSBitmapFormatAlphaFirst) ) { - // "from" is ARGB and "to" is RGBA --> convert "from" to RGBA - // it is enough to offset reading by one byte because A is always 0xFF - // so ARGBARGB becomes RGBARGB(A) as needed - from_data++; - } - int to_w = (int)[to pixelsWide]; // pixel width of "to" - int from_w = (int)[from pixelsWide]; // pixel width of "from" - int from_h = (int)[from pixelsHigh]; // pixel height of "from" - int to_depth = (int)[to samplesPerPixel]; - int from_depth = (int)[from samplesPerPixel]; - int depth = 0; - if (to_depth > from_depth) depth = from_depth; - else if (from_depth > to_depth) depth = to_depth; - float factor = to_w / (float)to_width; // scaling factor is 1 for classic displays and 2 for retina - to_x = factor*to_x; // transform offset from screen unit to pixels - to_y = factor*to_y; - // perform the copy - uchar *tobytes = [to bitmapData] + to_y * to_w * to_depth + to_x * to_depth; - const uchar *frombytes = from_data; - for (int i = 0; i < from_h; i++) { - if (depth == 0) { // depth is always 0 in case of RGBA <-> ARGB conversion - if (i == 0 && from_data < [from bitmapData]) { - memcpy(tobytes+1, frombytes+1, from_w * from_depth-1); // avoid reading before [from bitmapData] - *tobytes = 0xFF; // set the very first A byte - } else if (i == from_h - 1 && from_data > [from bitmapData]) { - memcpy(tobytes, frombytes, from_w * from_depth - 1); // avoid reading after end of [from bitmapData] - *(tobytes + from_w * from_depth - 1) = 0xFF; // set the very last A byte - } else { - memcpy(tobytes, frombytes, from_w * from_depth); - } - } else { - for (int j = 0; j < from_w; j++) { - memcpy(tobytes + j * to_depth, frombytes + j * from_depth, depth); - } - } - tobytes += to_w * to_depth; - frombytes += from_w * from_depth; - } -} - - -static NSBitmapImageRep* GL_rect_to_nsbitmap(Fl_Window *win, int x, int y, int w, int h) -// captures a rectangle from a GL window and returns it as an allocated NSBitmapImageRep -// the capture has high res on retina -{ - Fl_Device_Plugin *plugin = Fl_Device_Plugin::opengl_plugin(); - if (!plugin) return nil; - Fl_RGB_Image *img = plugin->rectangle_capture(win, x, y, w, h); - NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:img->w() pixelsHigh:img->h() bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:4*img->w() bitsPerPixel:32]; - if (img->d() == 4) memcpy([bitmap bitmapData], img->array, 4*img->data_w()*img->data_h()); - else { - memset([bitmap bitmapData], 0xFF, [bitmap bytesPerPlane]); - const uchar *from = img->array; - for (int r = 0; r < img->h(); r++) { - uchar *to = [bitmap bitmapData] + r * [bitmap bytesPerRow]; - for (int c = 0; c < img->w(); c++) { - memcpy(to, from, 3); - from += 3; - to += 4; - } - } - } - delete img; - return bitmap; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 -static NSBitmapImageRep* rect_to_NSBitmapImage_layer(Fl_Window *win, int x, int y, int w, int h) -{ // capture window data for layer-based views because initWithFocusedViewRect: does not work for them - FLView *view = (FLView*)[fl_xid(win) contentView]; - if (!view->aux_bitmap) return nil; - CGImageRef cgimg = CGBitmapContextCreateImage(view->aux_bitmap); - if (x || y || w != win->w() || h != win->h()) { - float s = Fl::screen_driver()->scale(0); - if (Fl_Cocoa_Window_Driver::driver(win)->mapped_to_retina()) s *= 2; - CGRect rect = CGRectMake(x * s, y * s, w * s, h * s); - CGImageRef cgimg2 = CGImageCreateWithImageInRect(cgimg, rect); - CGImageRelease(cgimg); - cgimg = cgimg2; - } - NSBitmapImageRep *bitmap = (cgimg ? [[NSBitmapImageRep alloc] initWithCGImage:cgimg/*10.5*/] : nil); - CGImageRelease(cgimg); - return bitmap; -} -#endif - -static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h) { - NSBitmapImageRep *bitmap = nil; - NSRect rect; - float s = Fl_Graphics_Driver::default_driver().scale(); - if (win->as_gl_window() && y >= 0) { - bitmap = GL_rect_to_nsbitmap(win, x, y, w, h); - } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - else if (views_use_CA) { - bitmap = rect_to_NSBitmapImage_layer(win, x, y, w, h); - } -#endif - else { - NSView *winview = nil; - if ( through_Fl_X_flush && Fl_Window::current() == win ) { - rect = NSMakeRect(x - 0.5, y - 0.5, w, h); - } - else { - winview = [fl_xid(win) contentView]; - int view_h = [winview frame].size.height; - rect = NSMakeRect(int(x*s), int(view_h-y*s-int(h*s)), int(w*s), int(h*s)); - // lock focus to win's view - if (fl_mac_os_version >= 101100) { - NSGraphicsContext *ctxt = [fl_xid(win) - performSelector:@selector(graphicsContext)]; - [ctxt saveGraphicsState]; // necessary under 10.11 - } - [winview performSelector:@selector(lockFocus)]; - } - // The image depth is 3 until macOS 10.5 and 4 with 10.6 and above - // the deprecation warning at 10.14 can be ignored because runs only for macOS < 10.14 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect]; -#pragma clang diagnostic pop - if ( !( through_Fl_X_flush && Fl_Window::current() == win) ) { - [winview performSelector:@selector(unlockFocus)]; - if (fl_mac_os_version >= 101100) { - NSGraphicsContext *ctxt = [fl_xid(win) - performSelector:@selector(graphicsContext)]; - [ctxt restoreGraphicsState]; - } - } - } - return bitmap; -} - -static NSBitmapImageRep* rect_to_NSBitmapImageRep_subwins(Fl_Window *win, int x, int y, int w, int h, bool capture_subwins) -/* Captures a rectangle from a mapped window. - On retina displays, the resulting bitmap has 2 pixels per screen unit. - The returned value is to be released after use - */ -{ - Fl_Rect r(x, y, w, h); - NSBitmapImageRep *bitmap = [fl_xid(win) rect_to_NSBitmapImageRep:&r]; - if (!capture_subwins || !bitmap) return bitmap; - - // capture also subwindows - NSArray *children = [fl_xid(win) childWindows]; // 10.2 - NSEnumerator *enumerator = [children objectEnumerator]; - id child; - while ((child = [enumerator nextObject]) != nil) { - if (![child isKindOfClass:[FLWindow class]]) continue; - Fl_Window *sub = [(FLWindow*)child getFl_Window]; - CGRect rsub = CGRectMake(sub->x(), win->h() -(sub->y()+sub->h()), sub->w(), sub->h()); - CGRect clip = CGRectMake(x, win->h()-(y+h), w, h); - clip = CGRectIntersection(rsub, clip); - if (CGRectIsNull(clip)) continue; - NSBitmapImageRep *childbitmap = rect_to_NSBitmapImageRep_subwins(sub, clip.origin.x - sub->x(), - win->h() - clip.origin.y - sub->y() - clip.size.height, clip.size.width, clip.size.height, true); - if (childbitmap) { - // if bitmap is high res and childbitmap is not, childbitmap must be rescaled - if (!win->as_gl_window() && Fl_Cocoa_Window_Driver::driver(win)->mapped_to_retina() && - sub->as_gl_window() && !Fl::use_high_res_GL()) { - childbitmap = scale_nsbitmapimagerep(childbitmap, 2); - } - float s = Fl_Graphics_Driver::default_driver().scale(); - write_bitmap_inside(bitmap, w * s, childbitmap, - (clip.origin.x - x) * s, - (win->h() - clip.origin.y - clip.size.height - y) * s ); - } - [childbitmap release]; - } - return bitmap; -} - -static void nsbitmapProviderReleaseData (void *info, const void *data, size_t size) -{ - [(NSBitmapImageRep*)info release]; -} - -CGImageRef Fl_Cocoa_Window_Driver::CGImage_from_window_rect(int x, int y, int w, int h, bool capture_subwins) -{ - /* Returns a capture of a rectangle of a mapped window as a CGImage. - With retina displays, the returned image has twice the width and height. - CFRelease the returned CGImageRef after use - */ - CGImageRef img; - NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep_subwins(pWindow, x, y, w, h, capture_subwins); - img = (CGImageRef)[bitmap performSelector:@selector(CGImage)]; // requires Mac OS 10.5 - CGImageRetain(img); - [bitmap release]; - return img; -} - -int Fl_Cocoa_Window_Driver::decorated_w() -{ - if (!shown() || parent() || !border() || !visible()) - return w(); - int bx=0; - get_window_frame_sizes(pWindow, &bx); - return w() + 2 * bx; -} - -int Fl_Cocoa_Window_Driver::decorated_h() -{ - if (!shown() || parent() || !border() || !visible()) - return h(); - int bx = 0, by = 0; - int bt = get_window_frame_sizes(pWindow, &bx, &by); - float s = Fl::screen_driver()->scale(0); - return h() + bt/s; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 && defined(__BLOCKS__) - -// Requires -weak_framework ScreenCaptureKit and used by FLTK for macOS ≥ 15.0 -static CGImageRef capture_decorated_window_SCK(NSWindow *nswin) { - if (@available(macOS 15.0, *)) { - __block CGImageRef capture = NULL; - __block BOOL capture_err = NO; - void (^block_to_stop_main_loop)(void) = ^{ CFRunLoopStop(CFRunLoopGetMain()); }; - // Fix for bug in ScreenCaptureKit that modifies a window's styleMask the first time - // it captures a non-resizable window. We memorize each non-resizable window's styleMask, - // and we restore modified styleMasks later, after the screen capture. - NSMutableArray *xid_array = [NSMutableArray arrayWithCapacity:2]; - NSMutableArray *mask_array = [NSMutableArray arrayWithCapacity:2]; - Fl_Window *win = Fl::first_window(); - while (win) { - if (!win->parent() && win->border()) { - FLWindow *xid = fl_mac_xid(win); - if (xid && !([xid styleMask] & NSWindowStyleMaskResizable)) { - [xid_array addObject:xid]; - NSUInteger mask = [xid styleMask]; - [mask_array addObject:[NSData dataWithBytes:&mask length:sizeof(NSUInteger)]]; - } - } - win = Fl::next_window(win); - } - CGWindowID target_id = (CGWindowID)[nswin windowNumber]; - NSRect r = [nswin frame]; - int W = r.size.width, H = r.size.height; - [SCShareableContent getCurrentProcessShareableContentWithCompletionHandler: // macOS 14.4 - ^(SCShareableContent *shareableContent, NSError *error) { - SCWindow *scwin = nil; - if (!error) { - NSEnumerator *enumerator = [[shareableContent windows] objectEnumerator]; - while ((scwin = (SCWindow*)[enumerator nextObject]) != nil) { - if ([scwin windowID] == target_id) { - break; - } - } - } - if (!scwin) { - capture_err = YES; - dispatch_async(dispatch_get_main_queue(), block_to_stop_main_loop); - return; - } - SCContentFilter *filter = [[[SCContentFilter alloc] initWithDesktopIndependentWindow:scwin] autorelease]; - int s = (int)[filter pointPixelScale]; - SCStreamConfiguration *config = [[[SCStreamConfiguration alloc] init] autorelease]; - [config setIgnoreShadowsSingleWindow:YES]; - [config setIgnoreShadowsDisplay:YES]; // necessary with macOS 26 Tahoe - [config setShowsCursor:NO]; - [config setWidth:W*s]; - [config setHeight:H*s]; - [config setIncludeChildWindows:NO]; // macOS 14.2 - [SCScreenshotManager captureImageWithFilter:filter - configuration:config - completionHandler:^(CGImageRef sampleBuffer, NSError *error) { - if (error) capture_err = YES; - else { - capture = sampleBuffer; - CGImageRetain(capture); - } - dispatch_async(dispatch_get_main_queue(), block_to_stop_main_loop); - } - ]; - } - ]; - // run the main loop until the 1 or 2 blocks above have completed and have stopped the loop - while (!capture_err && !capture) CFRunLoopRun(); - if (capture_err) return NULL; - // ScreenCaptureKit bug cont'd: restore modified styleMasks. - for (int i = 0, count = (int)[xid_array count]; i < count; i++) { - NSUInteger mask; - [(NSData*)[mask_array objectAtIndex:i] getBytes:&mask length:sizeof(NSUInteger)]; - NSWindow *xid = (NSWindow*)[xid_array objectAtIndex:i]; - if (mask != [xid styleMask]) [xid setStyleMask:mask]; - } - return capture; - } else return NULL; -} -#endif //MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 - - -CGImageRef Fl_Cocoa_Window_Driver::capture_decorated_window_10_5(NSWindow *nswin) { - // usable with 10.5 and above - CGImageRef img = NULL; -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0 && defined(__BLOCKS__) - if (fl_mac_os_version >= 150000) - img = capture_decorated_window_SCK(nswin); - else -#endif - { -# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_15_0 - NSInteger win_id = [nswin windowNumber]; - CFArrayRef array = CFArrayCreate(NULL, (const void**)&win_id, 1, NULL); - img = CGWindowListCreateImageFromArray(CGRectNull, array, kCGWindowImageBoundsIgnoreFraming); // 10.5 - CFRelease(array); -# endif - } - return img; -} - - -static CGImageRef capture_window_titlebar(Fl_Window *win, Fl_Cocoa_Window_Driver *cocoa_dr) { - CGImageRef img; - // verified OK from 10.6 - FLWindow *nswin = fl_xid(win); - CGImageRef img_full = Fl_Cocoa_Window_Driver::capture_decorated_window_10_5(nswin); - int bt = [nswin frame].size.height - [[nswin contentView] frame].size.height; - int s = CGImageGetWidth(img_full) / [nswin frame].size.width; - CGRect cgr = CGRectMake(0, 0, CGImageGetWidth(img_full), bt * s); - img = CGImageCreateWithImageInRect(img_full, cgr); // 10.4 - CGImageRelease(img_full); - return img; -} - - -void Fl_Cocoa_Window_Driver::draw_titlebar_to_context(CGContextRef gc, int w, int h) -{ - FLWindow *nswin = fl_xid(pWindow); - if ([nswin canBecomeMainWindow]) [nswin makeMainWindow]; - [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:nil inMode:NSDefaultRunLoopMode dequeue:NO]; - CGImageRef img = capture_window_titlebar(pWindow, this); - if (img) { - CGContextSaveGState(gc); - CGContextDrawImage(gc, CGRectMake(0, 0, w, h), img); - CGImageRelease(img); - CGContextRestoreGState(gc); - } -} - - -/* Returns the version of the running Mac OS as an int such as 100802 for 10.8.2, - and also assigns that value to global fl_mac_os_version. - N.B.: macOS "Big Sur" 11.0 can produce 2 different values for fl_mac_os_version: - - when SDK 11.0 is used, fl_mac_os_version is set to 110000 (or bigger) - - when SDK 10.15 or earlier is used, fl_mac_os_version is set to 101600 - That is reported to facilitate life of apps that assumed majorVersion would remain equal to 10 - and used only minorVersion to determine what is the running version of macOS. - */ -int Fl_Darwin_System_Driver::calc_mac_os_version() { - if (fl_mac_os_version) return fl_mac_os_version; - int M = 0, m = 0, b = 0; - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 - if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) { - NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; - M = (int)version.majorVersion; - m = (int)version.minorVersion; - b = (int)version.patchVersion; - } - else -#endif - { - NSDictionary * sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; - const char *s = [[sv objectForKey:@"ProductVersion"] UTF8String]; - sscanf(s, "%d.%d.%d", &M, &m, &b); - } - [localPool release]; - fl_mac_os_version = M*10000 + m*100 + b; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14 - if (fl_mac_os_version >= 101400) views_use_CA = YES; -#endif - if (fl_mac_os_version < 100700) - fprintf(stderr, "Warning: FLTK expects macOS version 10.7 or higher"); - return fl_mac_os_version; -} - -/* - Note: `prefs` can be NULL! - */ -char *Fl_Darwin_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_Preferences::Root root, - const char *vendor, const char *application) -{ - static char *filename = 0L; - - // Allocate this only when we need it, but then keep it allocated. - if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX); - - switch (root&Fl_Preferences::ROOT_MASK) { - case Fl_Preferences::SYSTEM: - // This is safe, even on machines that use different languages - strcpy(filename, "/Library/Preferences"); - break; - case Fl_Preferences::USER: - { // Find the home directory, but return NULL if components were not found. - // If we ever port this to iOS: NSHomeDirectory returns tha location of the app! - const char *e = ::getenv("HOME"); - // if $HOME does not exist, try NSHomeDirectory, the Mac way. - NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; - if ( (e==0L) || (e[0]==0) || (::access(e, F_OK)==-1) ) { - NSString *nsHome = NSHomeDirectory(); - if (nsHome) - e = [nsHome UTF8String]; - } - // if NSHomeDirectory does not work, try getpwuid(), the Unix way. - if ( (e==0L) || (e[0]==0) || (::access(e, F_OK)==-1) ) { - struct passwd *pw = getpwuid(getuid()); - e = pw->pw_dir; - } - snprintf(filename, FL_PATH_MAX, "%s/Library/Preferences", e); - [localPool release]; - break; } - } - - // Make sure that the parameters are not NULL - if ( (vendor==0L) || (vendor[0]==0) ) - vendor = "unknown"; - if ( (application==0L) || (application[0]==0) ) - application = "unknown"; - - // Our C path names for preferences will be: - // SYSTEM: "/Library/Preferences/$vendor/$application.prefs" - // USER: "/Users/$user/Library/Preferences/$vendor/$application.prefs" - snprintf(filename + strlen(filename), FL_PATH_MAX - strlen(filename), - "/%s/%s.prefs", vendor, application); - - return filename; -} - -Fl_Cocoa_Window_Driver::~Fl_Cocoa_Window_Driver() -{ - if (shape_data_) { - if (shape_data_->mask) { - CGImageRelease(shape_data_->mask); - } - delete shape_data_; - } - [icon_image release]; -} - -static NSImage* rgb_to_nsimage(const Fl_RGB_Image *rgb) { - if (!rgb) return nil; - int ld = rgb->ld(); - if (!ld) ld = rgb->data_w() * rgb->d(); - NSImage *win_icon = nil; - if (fl_mac_os_version >= 101000) { - NSBitmapImageRep *bitmap = - [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL - pixelsWide:rgb->data_w() - pixelsHigh:rgb->data_h() - bitsPerSample:8 - samplesPerPixel:rgb->d() - hasAlpha:!(rgb->d() & 1) - isPlanar:NO - colorSpaceName:(rgb->d() <= 2 ? NSDeviceWhiteColorSpace : - NSDeviceRGBColorSpace) - bitmapFormat:NSBitmapFormatAlphaNonpremultiplied - bytesPerRow:ld - bitsPerPixel:rgb->d() * 8]; // 10.4 - memcpy([bitmap bitmapData], rgb->array, rgb->data_h() * ld); - win_icon = [[NSImage alloc] initWithSize:NSMakeSize(0, 0)]; - [win_icon addRepresentation:bitmap]; - [bitmap release]; - } - return win_icon; -} - -void Fl_Cocoa_Window_Driver::icons(const Fl_RGB_Image *icons[], int count) { - [icon_image release]; - icon_image = nil; - if (count >= 1 && pWindow->border() && pWindow->label() && strlen(pWindow->label())) { - ((Fl_RGB_Image*)icons[0])->normalize(); - icon_image = rgb_to_nsimage(icons[0]); - } -} - -void Fl_Cocoa_Screen_Driver::default_icons(const Fl_RGB_Image *icons[], int count) { - [default_icon release]; - default_icon = nil; - if (count >= 1) { - default_icon = rgb_to_nsimage(icons[0]); - } -} - - -fl_uintptr_t Fl_Cocoa_Window_Driver::os_id() { - return [fl_xid(pWindow) windowNumber]; -} - - -// Deprecated in 1.4 - only for backward compatibility with 1.3 -void Fl::insertion_point_location(int x, int y, int height) { - Fl_Cocoa_Screen_Driver::insertion_point_location(x, y, height); -} -// Deprecated in 1.4 - only for backward compatibility with 1.3 -void Fl::reset_marked_text() { - Fl_Cocoa_Screen_Driver::reset_marked_text(); -} |
