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>
#import <Cocoa/Cocoa.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0
# import <ScreenCaptureKit/ScreenCaptureKit.h>
#endif
#ifdef DEBUG_SELECT
#include <stdio.h>
#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
extern void fl_fix_focus();
extern int fl_send_system_handlers(void *e);
static void createAppleMenu(void);
static void cocoaMouseHandler(NSEvent *theEvent);
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();
void *fl_capture = 0; FLWindow *fl_window;
static int main_screen_height; static BOOL through_drawRect = NO;
static BOOL through_Fl_X_flush = NO;
static BOOL views_use_CA = NO; static int im_enabled = -1;
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6) || defined(__POWERPC__)
# define NSPasteboardTypeTIFF @"public.tiff"
# define NSPasteboardTypePDF @"com.adobe.pdf"
# define NSPasteboardTypeString @"public.utf8-plain-text"
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Wunguarded-availability"
static NSString *TIFF_pasteboard_type = (fl_mac_os_version >= 100600 ? NSPasteboardTypeTIFF :
NSTIFFPboardType);
static NSString *PDF_pasteboard_type = (fl_mac_os_version >= 100600 ? NSPasteboardTypePDF :
NSPDFPboardType);
static NSString *PICT_pasteboard_type = (fl_mac_os_version >= 100600 ? @"com.apple.pict" :
NSPICTPboardType);
static NSString *UTF8_pasteboard_type = (fl_mac_os_version >= 100600 ? NSPasteboardTypeString :
NSStringPboardType);
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; static NSMutableArray *dropped_files_list = nil; typedef void (*open_cb_f_type)(const char *);
static Fl_Window *starting_moved_window = NULL;
enum { FLTKTimerEvent = 1, FLTKDataReadyEvent };
typedef void *TSMDocumentID;
extern "C" enum {
kTSMDocumentEnabledInputSourcesPropertyTag = 'enis' };
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;
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
const NSUInteger NSWindowStyleMaskFullScreen = NSFullScreenWindowMask;
# endif
const NSUInteger NSEventMaskAny = NSAnyEventMask;
const NSUInteger NSEventMaskSystemDefined = NSSystemDefinedMask;
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
const NSUInteger NSBitmapFormatAlphaFirst = NSAlphaFirstBitmapFormat;
const NSUInteger NSBitmapFormatAlphaNonpremultiplied = NSAlphaNonpremultipliedBitmapFormat;
# endif
#endif
static unsigned short* macKeyLookUp = NULL;
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;
return ret;
}
static void nothing() {}
void (*fl_lock_function)() = nothing;
void (*fl_unlock_function)() = nothing;
#define POLLIN 1
#define POLLOUT 4
#define POLLERR 8
class DataReady
{
struct FD
{
int fd;
short events;
void (*cb)(int, void*);
void* arg;
};
int nfds, fd_array_size;
FD *fds;
pthread_t tid;
pthread_mutex_t _datalock; fd_set _fdsets[3]; int _maxfd; int _cancelpipe[2];
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;
}
void DataLock() { pthread_mutex_lock(&_datalock); }
void DataUnlock() { pthread_mutex_unlock(&_datalock); }
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]); }
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(); 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();
if (events & POLLIN) FD_SET(n, &_fdsets[0]);
if (events & POLLOUT) FD_SET(n, &_fdsets[1]);
if (events & POLLERR) FD_SET(n, &_fdsets[2]);
if (n > _maxfd) _maxfd = n;
DataUnlock();
}
void DataReady::RemoveFD(int n, int events)
{
int i,j;
_maxfd = -1; for (i=j=0; i<nfds; i++) {
if (fds[i].fd == n) {
int e = fds[i].events & ~events;
if (!e) continue; fds[i].events = e;
}
if (fds[i].fd > _maxfd) _maxfd = fds[i].fd;
if (j<i) {
fds[j] = fds[i];
}
j++;
}
nfds = j;
DataLock();
if (events & POLLIN) FD_CLR(n, &_fdsets[0]);
if (events & POLLOUT) FD_CLR(n, &_fdsets[1]);
if (events & POLLERR) FD_CLR(n, &_fdsets[2]);
DataUnlock();
}
int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x)
{
int ret;
DataLock();
timeval t = { 0, 1 }; r = _fdsets[0], w = _fdsets[1], x = _fdsets[2];
ret = ::select(_maxfd+1, &r, &w, &x, &t);
DataUnlock();
if ( ret == -1 ) {
DEBUGPERRORMSG("CheckData(): select()");
}
return(ret);
}
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");
}
}
}
void* DataReady::DataReadyThread(void *o)
{
DataReady *self = (DataReady*)o;
while ( 1 ) { self->DataLock();
int maxfd = self->_maxfd;
fd_set r = self->GetFdset(0);
fd_set w = self->GetFdset(1);
fd_set x = self->GetFdset(2);
int cancelpipe = self->GetCancelPipe(0);
if ( cancelpipe > maxfd ) maxfd = cancelpipe;
FD_SET(cancelpipe, &r); FD_SET(cancelpipe, &x);
self->DataUnlock();
timeval t = { 2, 0 }; int ret = ::select(maxfd+1, &r, &w, &x, &t);
pthread_testcancel(); switch ( ret ) {
case 0: continue;
case -1: {
DEBUGPERRORMSG("CHILD THREAD: select() failed");
return(NULL); }
default: {
if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x)) { return(NULL); } 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); }
}
}
}
void DataReady::StartThread(void)
{
CancelThread(DEBUGTEXT("STARTING NEW THREAD\n"));
DataLock();
pipe(_cancelpipe); DataUnlock();
DEBUGMSG("*** START THREAD\n");
pthread_create(&tid, NULL, DataReadyThread, (void*)this);
}
void DataReady::CancelThread(const char *reason)
{
if ( tid ) {
DEBUGMSG("*** CANCEL THREAD: ");
DEBUGMSG(reason);
if ( pthread_cancel(tid) == 0 ) { DataLock();
write(_cancelpipe[1], "x", 1); DataUnlock();
pthread_join(tid, NULL); }
tid = 0;
DEBUGMSG("(JOINED) OK\n");
}
DataLock();
if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; }
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);
}
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"));
fd_set r,w,x;
switch(dataready.CheckData(r,w,x)) {
case 0: break;
case -1: break;
default: dataready.HandleData(r,w,x);
break;
}
fl_unlock_function();
return;
}
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;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
- (NSPoint)convertBaseToScreen:(NSPoint)aPoint;
#endif
- (NSBitmapImageRep*)rect_to_NSBitmapImageRep:(Fl_Rect*)r;
- (void)makeKeyWindow;
@end
@interface FLView : NSView <NSTextInput
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
, NSTextInputClient
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
,NSDraggingSource
#endif
> {
BOOL in_key_event; BOOL need_handle; NSInteger identifier;
NSRange selectedRange;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14
@public
CGContextRef aux_bitmap; #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;
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange;
- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection replacementRange:(NSRange)replacementRange;
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
- (NSInteger)windowLevel;
#else
- (void)updateTrackingAreas;
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context;
- (void)draggingSession:(NSDraggingSession *)session
endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation;
#endif
- (BOOL)did_view_resolution_change;
#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];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
while (fl_mac_os_version >= 100500) {
NSArray *a = [[self contentView] trackingAreas];
if ([a count] == 0) break;
NSTrackingArea *ta = (NSTrackingArea*)[a objectAtIndex:0];
[[self contentView] removeTrackingArea:ta];
}
#endif
w = NULL;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
- (NSPoint)convertBaseToScreen:(NSPoint)aPoint
{
if (fl_mac_os_version >= 100700) {
NSRect r = [self convertRectToScreen:NSMakeRect(aPoint.x, aPoint.y, 0, 0)];
return r.origin;
}
else {
typedef NSPoint (*convertIMP)(id, SEL, NSPoint);
static convertIMP addr = (convertIMP)[NSWindow instanceMethodForSelector:@selector(convertBaseToScreen:)];
return addr(self, @selector(convertBaseToScreen:), aPoint);
}
}
#endif
- (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;
if (fl_mac_os_version >= 100700) {
typedef void (*setIMP)(id, SEL, BOOL);
static setIMP addr = (setIMP)[NSWindow instanceMethodForSelector:@selector(setRestorable:)];
addr(self, @selector(setRestorable:), NO);
}
}
return self;
}
- (Fl_Window *)getFl_Window;
{
return w;
}
- (BOOL)canBecomeKeyWindow
{
if (Fl::modal_ && (Fl::modal_ != w))
return NO; return !(!w || w->output() || w->tooltip_window() || w->menu_window() || w->parent());
}
- (BOOL)canBecomeMainWindow
{
if (Fl::modal_ && (Fl::modal_ != w))
return NO;
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 { 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]) { FLWindow *pxid = fl_xid(parent);
[pxid addChildWindow:self ordered:NSWindowAbove]; [self orderWindow:NSWindowAbove relativeTo:[pxid windowNumber]]; }
}
- (void)checkSubwindowFrame {
if (!w->parent()) return;
Fl_Window *from = w, *parent;
CGRect full = CGRectMake(0, 0, w->w(), w->h()); CGRect srect = full; int fromx = 0, fromy = 0;
while ((parent = from->window()) != NULL) { fromx -= from->x(); fromy -= from->y();
CGRect prect = CGRectMake(fromx, fromy, parent->w(), parent->h());
srect = CGRectIntersection(prect, srect); from = parent;
}
Fl_Cocoa_Window_Driver *d = Fl_Cocoa_Window_Driver::driver(w);
CGRect *r = d->subRect();
CGRect current_clip = (r ? *r : full); if (!CGRectEqualToRect(srect, 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]; }
}
-(void)waitForExpose
{
if ([self getFl_Window]->shown()) {
NSModalSession session = [NSApp beginModalSessionForWindow:self];
[NSApp runModalSession:session];
[NSApp endModalSession:session];
}
}
- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
{
if ([self parentWindow]) return frameRect; return [super constrainFrameRect:frameRect toScreen:screen]; }
- (NSBitmapImageRep*)rect_to_NSBitmapImageRep:(Fl_Rect*)r {
return rect_to_NSBitmapImageRep(w, r->x(), r->y(), r->w(), r->h());
}
- (void)makeKeyWindow {
if ([self canBecomeKeyWindow]) [super makeKeyWindow];
}
@end
@interface FLApplication : NSObject
{
}
+ (void)sendEvent:(NSEvent *)theEvent;
@end
static int do_queued_events( double time = 0.0 )
{
static int got_events; got_events = 0;
if ( dataready.IsThreadRunning() ) {
dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n"));
}
if ( dataready.GetNfds() ) {
dataready.StartThread();
}
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]; time = 0;
}
fl_lock_function();
return got_events;
}
double Fl_Darwin_System_Driver::wait(double time_to_wait)
{
if (dropped_files_list) { drain_dropped_files_list();
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
time_to_wait = Fl_System_Driver::wait(time_to_wait);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (fl_mac_os_version < 101100) NSDisableScreenUpdates(); Fl::flush();
if (fl_mac_os_version < 101100) NSEnableScreenUpdates(); #pragma clang diagnostic pop
if (Fl::idle) 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;
}
static NSInteger modal_window_level(void)
{
NSInteger level;
level = max_normal_window_level();
if (level < NSStatusWindowLevel)
return NSStatusWindowLevel;
level += 2;
if (level > CGShieldingWindowLevel())
return CGShieldingWindowLevel();
return level;
}
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;
}
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];
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;
}
}
}
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);
}
static void cocoaMouseWheelHandler(NSEvent *theEvent)
{
fl_lock_function();
Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
Fl::first_window(window);
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);
if (edx>0.0f) dx++; else if (edx<0.0f) dx--;
if (edy>0.0f) dy++; else if (edy<0.0f) dy--;
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();
}
static void cocoaMagnifyHandler(NSEvent *theEvent)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
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; 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();
#endif
}
static void cocoaMouseHandler(NSEvent *theEvent)
{
static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2, FL_Button+4, FL_Button+5 };
static int px, py;
fl_lock_function();
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;
px = (int)pos.x; py = (int)pos.y;
if ([theEvent clickCount] > 1)
Fl::e_clicks++;
else
Fl::e_clicks = 0;
case NSEventTypeLeftMouseUp:
case NSEventTypeRightMouseUp:
case NSEventTypeOtherMouseUp:
if ( !window ) break;
if ( !sendEvent ) {
sendEvent = FL_RELEASE;
}
Fl::e_keysym = keysym[ btn ];
case NSEventTypeMouseMoved:
if ( !sendEvent ) {
sendEvent = FL_MOVE;
}
case NSEventTypeLeftMouseDragged:
case NSEventTypeRightMouseDragged:
case NSEventTypeOtherMouseDragged: {
if ( !sendEvent ) {
sendEvent = FL_MOVE; if (fabs(pos.x-px)>5 || fabs(pos.y-py)>5)
Fl::e_is_click = 0;
}
mods_to_e_state( mods );
update_e_xy_and_e_xy_root([theEvent window]);
if (fl_mac_os_version < 100500) {
Fl_Window *tooltip = Fl_Tooltip::current_window();
int inside = 0;
if (tooltip && tooltip->shown() ) { inside = (Fl::event_x_root() >= tooltip->x() && Fl::event_x_root() < tooltip->x() + tooltip->w() &&
Fl::event_y_root() >= tooltip->y() && Fl::event_y_root() < tooltip->y() + tooltip->h() );
}
if (inside)
window = tooltip;
}
Fl::handle( sendEvent, window );
}
break;
case NSEventTypeMouseEntered :
Fl::handle(FL_ENTER, window);
break;
case NSEventTypeMouseExited :
Fl::handle(FL_LEAVE, window);
break;
default:
break;
}
fl_unlock_function();
return;
}
@interface FLTextView : NSTextView {
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
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
<NSWindowDelegate>
#endif
+ (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
static void orderfront_subwindows(FLWindow *xid)
{
NSArray *children = [xid childWindows]; NSEnumerator *enumerator = [children objectEnumerator];
id child;
while ((child = [enumerator nextObject]) != nil) { [xid removeChildWindow:child];
[xid addChildWindow:child ordered:NSWindowAbove];
[child orderWindow:NSWindowAbove relativeTo:[xid windowNumber]];
orderfront_subwindows(child);
}
}
@interface FLWindowDelegateBefore10_6 : FLWindowDelegate
- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client;
@end
@implementation FLWindowDelegateBefore10_6
- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
{
return [FLTextView singleInstance];
}
@end
@interface FLWindowDelegateBefore10_5 : FLWindowDelegateBefore10_6
-(void)windowDidDeminiaturize:(NSNotification *)notif;
-(void)windowWillMiniaturize:(NSNotification *)notif;
@end
@implementation FLWindowDelegateBefore10_5
-(void)windowDidDeminiaturize:(NSNotification *)notif
{
[super windowDidDeminiaturize:notif];
fl_lock_function();
orderfront_subwindows([notif object]);
fl_unlock_function();
}
-(void)windowWillMiniaturize:(NSNotification *)notif
{
[super fl_windowMiniaturize:notif];
NSArray *children = [(NSWindow*)[notif object] childWindows]; NSEnumerator *enumerator = [children objectEnumerator];
id child;
while ((child = [enumerator nextObject]) != nil) [child orderOut:self];
}
@end
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();}
}
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]) {
if (fl_mac_os_version < 100500) flwindowdelegate_instance = [FLWindowDelegateBefore10_5 alloc];
else if (fl_mac_os_version < 100600) flwindowdelegate_instance = [FLWindowDelegateBefore10_6 alloc];
else 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]; }
#endif
if (window == starting_moved_window) {
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);
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();
}
- (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 {
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 {
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");
}
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];
if (window->fullscreen_active()) {
[nsw setLevel:NSNormalWindowLevel];
fixup_window_levels();
}
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];
if (w->fullscreen_active()
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
&& (fl_mac_os_version < 100700 || !(nsw.styleMask & NSWindowStyleMaskFullScreen))
#endif
) {
[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];
update_e_xy_and_e_xy_root(nsw);
if (fl_sys_menu_bar && Fl_MacOS_Sys_Menu_Bar_Driver::window_menu_style()) {
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()) {
item->setonly();
fl_sys_menu_bar->update();
}
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);
[nsw recursivelySendToSubwindows:@selector(setSubwindowFrame) applyToSelf:YES];
update_e_xy_and_e_xy_root(nsw);
Fl::flush(); fl_unlock_function();
}
- (void)fl_windowMiniaturize:(NSNotification *)notif
{
fl_lock_function();
FLWindow *nsw = (FLWindow*)[notif object];
if ([[nsw childWindows] count]) {
Fl_Window *window = [nsw getFl_Window];
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
{
if (fl_mac_os_version >= 100500) [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); fl_unlock_function();
return NO;
}
- (void)anyWindowWillClose:(NSNotification *)notif
{
fl_lock_function();
if ([[notif object] isKeyWindow]) {
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
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
<NSApplicationDelegate>
#endif
{
@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 {
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); Fl::handle(FL_CLOSE, win);
if (wt.exists() && win->shown()) { break;
}
}
fl_unlock_function();
if ( ! Fl::first_window() ) {
Fl::program_should_quit(1);
Fl_Cocoa_Screen_Driver::breakMacEventLoop(); }
return NSTerminateCancel;
}
- (void)applicationDidBecomeActive:(NSNotification *)notify
{
fl_lock_function();
clipboard_check();
fixup_window_levels();
fl_unlock_function();
}
- (void)applicationDidChangeScreenParameters:(NSNotification *)unused
{ 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;
newDoc = TSMGetActiveDocument();
if (newDoc != currentDoc) {
TSMDocumentID doc;
doc = TSMGetActiveDocument();
if (im_enabled)
TSMRemoveDocumentProperty(doc, kTSMDocumentEnabledInputSourcesPropertyTag);
else {
CFArrayRef inputSources;
CFDictionaryRef filter;
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;
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;
}
}
}
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]];
}
}
}
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_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) {
Fl_Window *firstw = Fl::first_window();
if (firstw) firstw->wait_for_expose();
} else if (in_nsapp_run) { 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(); fl_unlock_function();
return YES;
}
return NO;
}
@end
@interface FLAppDelegateBefore10_5 : FLAppDelegate
- (void)applicationDidUnhide:(NSNotification *)notify;
- (void)applicationDidUpdate:(NSNotification *)aNotification;
@end
@implementation FLAppDelegateBefore10_5
- (void)applicationDidUnhide:(NSNotification *)notify
{ fl_lock_function();
for (Fl_X *x = Fl_X::first; x; x = x->next) {
if (![(FLWindow*)x->xid parentWindow]) {
orderfront_subwindows((FLWindow*)x->xid);
}
}
fl_unlock_function();
}
- (void)applicationDidUpdate:(NSNotification *)aNotification
{
}
@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);
}
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]) {
cocoaMouseHandler(theEvent);
}
}
fl_unlock_function();
} else if (type == NSEventTypeApplicationDefined) {
if ([theEvent subtype] == FLTKDataReadyEvent) {
processFLTKEvent();
}
return;
} else if (type == NSEventTypeKeyUp) {
[[NSApp keyWindow] sendEvent:theEvent];
return;
}
[NSApp sendEvent:theEvent];
}
@end
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];
if ([bpath isEqualToString:exe] || [bpath isEqualToString:exe_dir]) value = 0;
} else value = 0;
}
return value == 1;
}
static void foreground_and_activate() {
if ( !is_bundled() ) { ProcessSerialNumber cur_psn = { 0, kCurrentProcess };
TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication);
}
[NSApp activateIgnoringOtherApps:YES];
}
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]; FLAppDelegate *delegate = (Fl_Darwin_System_Driver::calc_mac_os_version() < 100500 ? [FLAppDelegateBefore10_5 alloc] : [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];
if (!is_bundled()) [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:NO];
}
}
if (fl_mac_os_version < 140000) {
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]) {
[NSThread detachNewThreadSelector:@selector(doNothing:) toTarget:[FLWindowDelegate singleInstance] withObject:nil];
}
(void)localPool; }
}
static int input_method_startup()
{
static int retval = -1; if (retval == -1) {
fl_open_display();
if (fl_mac_os_version >= 100500) {
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");
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);
} else {
KeyScript = (KeyScript_type)Fl_Darwin_System_Driver::get_carbon_function("KeyScript");
retval = (KeyScript? 1 : 0);
}
}
return retval;
}
void Fl_Cocoa_Screen_Driver::enable_im() {
if (!input_method_startup()) return;
im_enabled = 1;
if (fl_mac_os_version >= 100500) {
((FLAppDelegate*)[NSApp delegate])->currentDoc = NULL;
[NSApp updateWindows]; }
else
KeyScript(-7);
}
void Fl_Cocoa_Screen_Driver::disable_im() {
if (!input_method_startup()) return;
im_enabled = 0;
if (fl_mac_os_version >= 100500) {
((FLAppDelegate*)[NSApp delegate])->currentDoc = NULL;
[NSApp updateWindows]; }
else
KeyScript(smEnableRomanKybdsOnly);
}
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;
}
int Fl_Cocoa_Screen_Driver::x() {
open_display();
return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].origin.x) / scale(0);
}
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);
}
int Fl_Cocoa_Screen_Driver::w() {
open_display();
return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].size.width) / scale(0);
}
int Fl_Cocoa_Screen_Driver::h() {
open_display();
return int([[[NSScreen screens] objectAtIndex:0] visibleFrame].size.height) / scale(0);
}
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);
}
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;
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;
int R = X+W, B = Y+H; int cx = (X+R)/2, cy = (Y+B)/2; 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); 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 (!gd) {
for( i = 0; i < count; i++) {
r = [[a objectAtIndex:i] frame];
r.origin.y = main_screen_height - (r.origin.y + r.size.height); 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 (!gd) {
for( i = 0; i < count; i++) {
r = [[a objectAtIndex:i] frame];
r.origin.y = main_screen_height - (r.origin.y + r.size.height); 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 (!gd) {
for( i = 0; i < count; i++) {
r = [[a objectAtIndex:i] frame];
r.origin.y = main_screen_height - (r.origin.y + r.size.height); 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];
}
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); 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 (!gd) gd = [a objectAtIndex:0];
if (gd) {
r = [gd visibleFrame];
r.origin.y = main_screen_height - (r.origin.y + r.size.height); 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);
}
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) { 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);
}
}
}
static void cocoaKeyboardHandler(NSEvent *theEvent)
{
NSUInteger mods;
mods = [theEvent modifierFlags];
UInt32 keyCode = 0, maskedKeyCode = 0;
unsigned short sym = 0;
keyCode = [theEvent keyCode];
maskedKeyCode = keyCode & 0x7f;
mods_to_e_state( mods ); if (!macKeyLookUp) macKeyLookUp = Fl_Darwin_System_Driver::compute_macKeyLookUp();
sym = macKeyLookUp[maskedKeyCode];
if (sym < 0xff00) { NSString *sim = [theEvent charactersIgnoringModifiers];
UniChar one;
CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one);
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;
Fl::e_length = 0;
Fl::e_text = (char*)"";
}
@interface FLTextInputContext : NSObject + (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 FLView
- (BOOL)did_view_resolution_change {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (fl_mac_os_version >= 100700) { 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)]; 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;
}
#endif
return NO;
}
#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) {
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
{
id o = fl_mac_os_version >= 100600 ? [self performSelector:@selector(inputContext)] : [FLTextInputContext singleInstance];
return [o handleEvent:theEvent];
}
- (id)initWithFrame:(NSRect)frameRect
{
static NSInteger counter = 0;
self = [super initWithFrame:frameRect];
if (self) {
in_key_event = NO;
identifier = ++counter;
}
return self;
}
- (void)drawRect:(NSRect)rect
{
FLWindow *cw = (FLWindow*)[self window];
Fl_Window *window = [cw getFl_Window];
if (!window) return; if (!Fl_X::flx(window)) return; 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) { window->size(window->w(), window->h()); }
}
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) { 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; }
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent
{
fl_lock_function();
cocoaKeyboardHandler(theEvent);
BOOL handled;
NSUInteger mods = [theEvent modifierFlags];
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]; }
[FLView prepareEtext:s];
Fl::compose_state = 0;
handled = Fl::handle(FL_KEYBOARD, w);
}
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; 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 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
- (void)updateTrackingAreas {
if (fl_mac_os_version >= 100500) {
if (![[self window] parentWindow]) {
while (true) {
NSArray *a = [self trackingAreas]; if ([a count] == 0) break;
NSTrackingArea *ta = (NSTrackingArea*)[a objectAtIndex:0];
[self removeTrackingArea:ta]; }
NSTrackingArea *tracking = [[[NSTrackingArea alloc] initWithRect:[self frame]
options:NSTrackingActiveAlways |
NSTrackingMouseEnteredAndExited |
NSTrackingMouseMoved
owner:self
userInfo:nil] autorelease];
if (tracking) {
[self addTrackingArea:tracking]; }
}
[super updateTrackingAreas];
}
}
#endif
- (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 {
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()) { 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 {
if (![[theEvent window] isKindOfClass:[FLWindow class]]) 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();
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;
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]; NSEnumerator *enumerator = [a objectEnumerator];
NSURL *url;
while ((url = (NSURL*)[enumerator nextObject]) != nil) {
const char *p = [url fileSystemRepresentation]; 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 {
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 {
NSString *newstring = [[NSString stringWithUTF8String:Fl::e_text] stringByAppendingString:aString];
[FLView prepareEtext:newstring];
}
- (void)doCommandBySelector:(SEL)aSelector {
NSString *s = [[NSApp currentEvent] characters];
s = [s substringFromIndex:[s length] - 1];
[FLView prepareEtext:s]; 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;
}
fl_lock_function();
Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
if (fl_mac_os_version >= 101400 && replacementRange.length > 0) {
[[self inputContext] discardMarkedText];
}
#endif
while (replacementRange.length--) { 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) {
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;
BOOL palette = !(in_key_event || Fl::compose_state);
if (palette) Fl::e_keysym = 0;
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);
if (!in_key_event || !has_text_key) {
Fl::handle(FL_KEYBOARD, target);
Fl::e_length = 0;
}
else need_handle = YES;
selectedRange = NSMakeRange(100, 0); if (palette) Fl::flush();
if (fl_mac_os_version < 100600) [[FLTextView singleInstance] setActive:NO];
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();
Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
while (replacementRange.length--) { 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'; }
if (in_key_event && Fl_Cocoa_Screen_Driver::next_marked_length && Fl::e_length) {
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();
}
- (NSRange)selectedRange {
Fl_Widget *w = Fl::focus();
if (w && w->use_accents_menu()) return selectedRange;
return NSMakeRange(NSNotFound, 0);
}
- (NSRange)markedRange {
return NSMakeRange(Fl::compose_state > 0?0:NSNotFound, Fl::compose_state);
}
- (BOOL)hasMarkedText {
return (Fl::compose_state > 0);
}
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
return [self attributedSubstringForProposedRange:aRange actualRange:NULL];
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
return nil;
}
- (NSArray *)validAttributesForMarkedText {
return nil;
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange {
return [self firstRectForCharacterRange:aRange actualRange:NULL];
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)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();
}
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];
}
- (
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
NSInteger
#else
long
#endif
)conversationIdentifier {
return identifier;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
- (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 );
}
}
#endif
@end
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;
}
}
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(); 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()) { 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)) {
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; x->region = 0;
subRect(0);
gc = 0;
mapped_to_retina(false);
changed_resolution(false);
NSRect crect;
if (w->fullscreen_active() && fl_mac_os_version < 100700) {
int top, bottom, left, right;
int sx, sy, sw, sh, X, Y, W, H;
top = fullscreen_screen_top();
bottom = fullscreen_screen_bottom();
left = fullscreen_screen_left();
right = fullscreen_screen_right();
if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
top = Fl::screen_num(w->x(), w->y(), w->w(), 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;
w->resize(X, Y, W, H);
winstyle = NSWindowStyleMaskBorderless;
winlevel = NSStatusWindowLevel;
}
float s = Fl::screen_driver()->scale(0);
crect.origin.x = round(s * w->x()); 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]; [cw setBackgroundColor:[NSColor clearColor]]; }
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; 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]; this->x(round(crect.origin.x/s));
this->y( round((main_screen_height - crect.origin.y)/s) - w->h() );
}
if(w->menu_window()) { [cw setAlphaValue:0.97];
}
[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() != &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); if (fl_mac_os_version < 101300) { [cw recursivelySendToSubwindows:@selector(display) applyToSelf:YES];
}
[cw miniaturize:nil];
} else if (w->parent()) { [cw setIgnoresMouseEvents:YES]; [cw setOpaque:NO];
[cw setBackgroundColor:[NSColor clearColor]]; starting_moved_window = w;
[cw setSubwindowFrame];
starting_moved_window = NULL;
FLWindow *pxid = fl_xid(w->top_window());
[pxid makeFirstResponder:[pxid contentView]];
} else { if ([cw canBecomeKeyWindow]) [cw makeKeyAndOrderFront:nil];
else [cw orderFront:nil];
if (w->fullscreen_active() && fl_mac_os_version >= 100700) {
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->parent()) [myview did_view_resolution_change]; [pool release];
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
static BOOL fullscreen_screen_border = NO; #endif
static NSUInteger calc_win_style(Fl_Window *win);
void Fl_Cocoa_Window_Driver::fullscreen_on() {
pWindow->_set_fullscreen();
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
bool has_border = pWindow->border();
if (fl_mac_os_version >= 100700 && fullscreen_screen_top() >= 0 && has_border) {
fullscreen_screen_border = YES;
has_border = false;
}
if (fl_mac_os_version >= 100700 && 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) { pWindow->_clear_fullscreen();
[nswin setLevel:NSNormalWindowLevel];
[nswin setStyleMask:calc_win_style(pWindow)]; pWindow->_set_fullscreen();
}
[nswin toggleFullScreen:nil];
} else
# endif
if (fl_mac_os_version >= 100600) {
FLWindow *nswin = fl_xid(pWindow);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (fl_mac_os_version >= 100700 && (nswin.styleMask & NSWindowStyleMaskFullScreen)) {
#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();
}
#endif
[nswin setStyleMask:NSWindowStyleMaskBorderless]; 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);
} else
#endif
{ pWindow->hide();
pWindow->show();
}
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();
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
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];
}
}
}
#endif
void Fl_Cocoa_Window_Driver::fullscreen_off(int X, int Y, int W, int H) {
NSWindow *nswin = fl_xid(pWindow);
pWindow->_clear_fullscreen();
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (fl_mac_os_version >= 100700 && ([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
# endif
if (fl_mac_os_version >= 100600) {
NSInteger level = NSNormalWindowLevel;
if (pWindow->modal()) level = modal_window_level();
else if (pWindow->non_modal()) level = non_modal_window_level();
BOOL has_focus = [nswin isKeyWindow];
[nswin orderOut:nil];
[nswin setLevel:level];
[nswin setStyleMask:calc_win_style(pWindow)]; 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];
} else
#endif
{
pWindow->hide();
pWindow->resize(X, Y, W, H);
pWindow->show();
}
Fl::handle(FL_FULLSCREEN, pWindow);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
fullscreen_screen_border = NO;
#endif
}
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 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
if (fl_mac_os_version >= 100600) {
if (pWindow->fullscreen_active() || pWindow->maximize_active()) {
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)]; if (border()) restore_window_title_and_icon(pWindow, icon_image);
pWindow->redraw();
}
else
#endif
Fl_Window_Driver::use_border();
}
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();
}
}
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);
}
}
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();
}
}
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); pWindow->redraw();
} else {
if (!Fl_Window::is_a_rescale())
pWindow->Fl_Group::resize(X, Y, W, H);
[xid setFrame:r display:YES];
[[xid contentView] displayIfNeededIgnoringOpacity];
pWindow->redraw();
}
}
else {
if (pWindow->parent()) starting_moved_window = pWindow;
if (!NSEqualPoints([xid frame].origin, pt))
[xid setFrameOrigin:pt]; else {
x(X); y(Y);
}
if (pWindow->parent()) starting_moved_window = NULL;
}
through_resize(0);
}
if (pWindow->parent()) [fl_xid(pWindow) checkSubwindowFrame];
}
void Fl_Cocoa_Window_Driver::make_current()
{
q_release_context();
Fl_X *i = Fl_X::flx(pWindow);
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) { [view setNeedsDisplay:YES];
}
if (views_use_CA && view->aux_bitmap) {
gc = view->aux_bitmap;
} else
#endif
{
NSGraphicsContext *nsgc = (through_drawRect ? [NSGraphicsContext currentContext] :
[NSGraphicsContext graphicsContextWithWindow:fl_window]);
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); #endif
CGContextSaveGState(gc); 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); CGContextScaleCTM(gc, s, s); if (subRect()) {
CGContextClipToRect(gc, CGRectOffset(*(subRect()), -0.5, -0.5));
}
CGContextSaveGState(gc);
fl_clip_region( 0 );
#ifdef FLTK_HAVE_CAIROEXT
if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow);
#endif
}
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); 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) {
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]]; [[NSColor clearColor] set];
NSRect r = NSMakeRect(0, 0, width, height);
NSRectFill(r);
[image drawInRect:dest_r]; [NSGraphicsContext restoreGraphicsState];
[localPool release];
} else
#endif
{
[image lockFocus];
#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);
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
if (fl_mac_os_version >= 100500) CGPDFContextClose(gc); #endif
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];
NSBitmapImageRep *bitmap = pdf_to_nsbitmapimagerep((NSData*)pdfdata);
CFRelease(pdfdata);
[clip setData:[bitmap TIFFRepresentation] forType:TIFF_pasteboard_type];
[bitmap release];
delete driver();
}
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;
}
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;
resize_selection_buffer(len+1, clipboard);
memcpy(fl_selection_buffer[clipboard], stuff, len);
fl_selection_buffer[clipboard][len] = 0; fl_selection_length[clipboard] = len;
if (clipboard) {
CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len);
if (text==NULL) return; 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); 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]; 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++) { 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;
}
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]; 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]) { [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]; [xid orderOut:nil];
}
}
void Fl_Cocoa_Window_Driver::iconize() {
[fl_xid(pWindow) miniaturize:nil];
}
static NSImage *CGBitmapContextToNSImage(CGContextRef c)
{
NSImage* image;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
if (fl_mac_os_version >= 100600) {
CGImageRef cgimg = CGBitmapContextCreateImage(c); image = [[NSImage alloc] initWithCGImage:cgimg size:NSZeroSize]; CFRelease(cgimg);
}
else
#endif
{
NSBitmapImageRep *imagerep =
[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:CGBitmapContextGetWidth(c)
pixelsHigh:CGBitmapContextGetHeight(c)
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:CGBitmapContextGetBytesPerRow(c)
bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)];
memcpy([imagerep bitmapData], CGBitmapContextGetData(c),
[imagerep bytesPerRow] * [imagerep pixelsHigh]);
image = [[NSImage alloc] initWithSize:NSMakeSize([imagerep pixelsWide],
[imagerep pixelsHigh])];
[image addRepresentation:imagerep];
[imagerep release];
}
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;
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)];
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 {
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];
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
{ 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:@""];
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]];
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];
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]];
}
if (fl_mac_os_version >= 100400) { 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]];
title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::hide],nil), nsappname];
[appleMenu addItemWithTitle:title
action:@selector(hide:)
keyEquivalent:@"h"];
menuItem = [appleMenu
addItemWithTitle:NSLocalizedString(
[NSString stringWithUTF8String:Fl_Mac_App_Menu::hide_others] , nil)
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
[menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)];
[appleMenu addItemWithTitle:NSLocalizedString(
[NSString stringWithUTF8String:Fl_Mac_App_Menu::show], nil)
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
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];
}
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
[menuItem setSubmenu:appleMenu];
mainmenu = [[NSMenu alloc] initWithTitle:@""];
[mainmenu addItem:menuItem];
if (fl_mac_os_version < 100600) {
[NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu];
}
[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 version_threshold = 100700;
int width, height;
if (fl_mac_os_version >= version_threshold) {
width = 50; height = 40;
}
else {
width = 16; height = 16;
}
Fl_Image_Surface *off = new Fl_Image_Surface(width, height, 1);
Fl_Surface_Device::push_current(off);
if (fl_mac_os_version >= version_threshold) {
fl_font(FL_HELVETICA, 20);
fl_color(FL_BLACK);
char str[4];
int l = fl_utf8encode(0x1F69A, str);
fl_draw(str, l, 1, 16);
}
else { CGContextSetRGBFillColor( (CGContextRef)off->offscreen(), 0,0,0,0);
fl_rectf(0,0,width,height);
CGContextSetRGBStrokeColor( (CGContextRef)off->offscreen(), 0,0,0,0.6);
fl_rect(0,0,width,height);
fl_rect(2,2,width-4,height-4);
}
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;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
if (fl_mac_os_version >= 100700) {
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];
} else
#endif
{
static NSSize offset={0,0};
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSPasteboard *mypasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
[mypasteboard declareTypes:[NSArray arrayWithObject:UTF8_pasteboard_type] owner:nil];
[mypasteboard setData:(NSData*)text forType:UTF8_pasteboard_type];
[myview dragImage:image at:pt offset:offset event:theEvent pasteboard:mypasteboard
source:myview slideBack:YES];
#pragma clang diagnostic pop
if ( w ) {
int old_event = Fl::e_number;
w->handle(Fl::e_number = FL_RELEASE);
Fl::e_number = old_event;
Fl::pushed( 0 );
}
}
CFRelease(text);
[localPool release];
return true;
}
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)
{
const uchar *from_data = [from bitmapData];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (fl_mac_os_version >= 100400) { if (([to bitmapFormat] & NSBitmapFormatAlphaFirst) &&
!([from bitmapFormat] & NSBitmapFormatAlphaFirst) ) {
from_data--;
} else if ( !([to bitmapFormat] & NSBitmapFormatAlphaFirst) && ([from bitmapFormat] & NSBitmapFormatAlphaFirst) ) {
from_data++;
}
}
#endif
int to_w = (int)[to pixelsWide]; int from_w = (int)[from pixelsWide]; int from_h = (int)[from pixelsHigh]; 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; to_x = factor*to_x; to_y = factor*to_y;
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) { if (i == 0 && from_data < [from bitmapData]) {
memcpy(tobytes+1, frombytes+1, from_w * from_depth-1); *tobytes = 0xFF; } else if (i == from_h - 1 && from_data > [from bitmapData]) {
memcpy(tobytes, frombytes, from_w * from_depth - 1); *(tobytes + from_w * from_depth - 1) = 0xFF; } 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)
{
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)
{ 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] : 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));
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (fl_mac_os_version >= 101100) {
NSGraphicsContext *ctxt = [fl_xid(win)
performSelector:@selector(graphicsContext)];
[ctxt saveGraphicsState]; }
#endif
[winview performSelector:@selector(lockFocus)];
}
#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 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (fl_mac_os_version >= 101100) {
NSGraphicsContext *ctxt = [fl_xid(win)
performSelector:@selector(graphicsContext)];
[ctxt restoreGraphicsState];
}
#endif
}
}
return bitmap;
}
static NSBitmapImageRep* rect_to_NSBitmapImageRep_subwins(Fl_Window *win, int x, int y, int w, int h, bool capture_subwins)
{
Fl_Rect r(x, y, w, h);
NSBitmapImageRep *bitmap = [fl_xid(win) rect_to_NSBitmapImageRep:&r];
if (!capture_subwins || !bitmap) return bitmap;
NSArray *children = [fl_xid(win) childWindows]; 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 (!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)
{
CGImageRef img;
NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep_subwins(pWindow, x, y, w, h, capture_subwins);
if (fl_mac_os_version >= 100500) {
img = (CGImageRef)[bitmap performSelector:@selector(CGImage)]; CGImageRetain(img);
[bitmap release];
} else {
CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider =
CGDataProviderCreateWithData(bitmap, [bitmap bitmapData],
[bitmap bytesPerRow] * [bitmap pixelsHigh],
nsbitmapProviderReleaseData);
img = CGImageCreate([bitmap pixelsWide], [bitmap pixelsHigh], 8, [bitmap bitsPerPixel],
[bitmap bytesPerRow], cspace,
([bitmap bitsPerPixel] == 32 ? kCGImageAlphaPremultipliedLast :
kCGImageAlphaNone) ,
provider, NULL, false, kCGRenderingIntentDefault);
CGColorSpaceRelease(cspace);
CGDataProviderRelease(provider);
}
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__)
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()); };
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 = [nswin windowNumber];
NSRect r = [nswin frame];
int W = r.size.width, H = r.size.height;
[SCShareableContent getCurrentProcessShareableContentWithCompletionHandler: ^(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]; [config setShowsCursor:NO];
[config setWidth:W*s];
[config setHeight:H*s];
[config setIncludeChildWindows:NO]; [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);
}
];
}
];
while (!capture_err && !capture) CFRunLoopRun();
if (capture_err) return NULL;
for (int i = 0, count = [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
CGImageRef Fl_Cocoa_Window_Driver::capture_decorated_window_10_5(NSWindow *nswin) {
CGImageRef img = NULL;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
# 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); CFRelease(array);
# endif
}
#endif return img;
}
static CGImageRef capture_window_titlebar(Fl_Window *win, Fl_Cocoa_Window_Driver *cocoa_dr) {
CGImageRef img;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
if (fl_mac_os_version >= 100600) { 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); CGImageRelease(img_full);
} else
#endif
{
int w = win->w(), h = win->decorated_h() - win->h();
Fl_Graphics_Driver::default_driver().scale(1);
img = cocoa_dr->CGImage_from_window_rect(0, -h, w, h, false);
Fl_Graphics_Driver::default_driver().scale(Fl::screen_driver()->scale(win->screen_num()));
}
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);
if (fl_mac_os_version < 100600) clip_to_rounded_corners(gc, w, h);
CGContextDrawImage(gc, CGRectMake(0, 0, w, h), img);
CGImageRelease(img);
CGContextRestoreGState(gc);
}
}
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
return fl_mac_os_version;
}
char *Fl_Darwin_System_Driver::preference_rootnode(Fl_Preferences * , Fl_Preferences::Root root,
const char *vendor, const char *application)
{
static char *filename = 0L;
if (!filename) filename = (char*)::calloc(1, FL_PATH_MAX);
switch (root&Fl_Preferences::ROOT_MASK) {
case Fl_Preferences::SYSTEM:
strcpy(filename, "/Library/Preferences");
break;
case Fl_Preferences::USER:
{ const char *e = ::getenv("HOME");
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 ( (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; }
}
if ( (vendor==0L) || (vendor[0]==0) )
vendor = "unknown";
if ( (application==0L) || (application[0]==0) )
application = "unknown";
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 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
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]; memcpy([bitmap bitmapData], rgb->array, rgb->data_h() * ld);
win_icon = [[NSImage alloc] initWithSize:NSMakeSize(0, 0)];
[win_icon addRepresentation:bitmap];
[bitmap release];
}
#endif
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];
}
void Fl::insertion_point_location(int x, int y, int height) {
Fl_Cocoa_Screen_Driver::insertion_point_location(x, y, height);
}
void Fl::reset_marked_text() {
Fl_Cocoa_Screen_Driver::reset_marked_text();
}