mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
454 lines
11 KiB
Text
454 lines
11 KiB
Text
#include "CocoMM.h"
|
|
|
|
#define LLOG(x) // DLOG(x)
|
|
|
|
#ifdef GUI_COCOA
|
|
|
|
@interface AppDelegate : NSObject<NSApplicationDelegate>
|
|
{
|
|
}
|
|
@end
|
|
|
|
namespace Upp {
|
|
NSMenu *Cocoa_DockMenu();
|
|
};
|
|
|
|
@implementation AppDelegate
|
|
- (NSMenu *)applicationDockMenu:(NSApplication *)sender
|
|
{
|
|
Upp::GuiLock __;
|
|
return Upp::Cocoa_DockMenu();
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
{
|
|
Upp::GuiLock __;
|
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(themeChanged:)
|
|
name:@"AppleColorPreferencesChangedNotification"
|
|
object:nil];
|
|
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(themeChanged:)
|
|
name: @"AppleInterfaceThemeChangedNotification"
|
|
object:nil];
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
|
{
|
|
Upp::GuiLock __;
|
|
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
|
|
}
|
|
|
|
- (void)themeChanged:(NSNotification *)aNotification
|
|
{
|
|
Upp::GuiLock __;
|
|
Upp::Ctrl::PostReSkin();
|
|
}
|
|
|
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
|
Upp::GuiLock __;
|
|
Upp::TopWindow::ShutdownWindows();
|
|
return NSTerminateCancel;
|
|
}
|
|
|
|
@end
|
|
|
|
namespace Upp {
|
|
|
|
static NSAutoreleasePool *main_coco_pool;
|
|
|
|
void SyncPopupFocus(NSWindow *win)
|
|
{
|
|
Ctrl *q = Ctrl::GetFocusCtrl();
|
|
if(q) {
|
|
q = q->GetTopCtrl();
|
|
if(q->IsPopUp() && q->GetNSWindow() != win && q->IsCocoActive()) {
|
|
q = q->GetOwner();
|
|
if(q) q->SetFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
extern const char *sClipFmtsRTF;
|
|
|
|
id menubar;
|
|
|
|
void CocoInit(int argc, const char **argv, const char **envptr)
|
|
{
|
|
Ctrl::GlobalBackBuffer();
|
|
main_coco_pool = [NSAutoreleasePool new];
|
|
|
|
[NSApplication sharedApplication];
|
|
|
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
|
|
menubar = [[NSMenu new] autorelease];
|
|
id appMenuItem = [[NSMenuItem new] autorelease];
|
|
[menubar addItem:appMenuItem];
|
|
id appMenu = [[NSMenu new] autorelease];
|
|
id appName = [[NSProcessInfo processInfo] processName];
|
|
id quitTitle = [@"Quit " stringByAppendingString:appName];
|
|
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
|
|
action:@selector(terminate:)
|
|
keyEquivalent:@"q"] autorelease];
|
|
[appMenu addItem:quitMenuItem];
|
|
[appMenuItem setSubmenu:appMenu];
|
|
|
|
[NSApp setMainMenu:menubar];
|
|
|
|
[NSApp setDelegate:[[AppDelegate alloc] init]];
|
|
|
|
NSFont *sysfont = [NSFont systemFontOfSize:0];
|
|
Font::SetFace(0, ToString((CFStringRef)[sysfont familyName]), Font::TTF);
|
|
|
|
Ctrl::SetUHDEnabled(true);
|
|
bool uhd = true;
|
|
for (NSScreen *screen in [NSScreen screens]) {
|
|
if([screen backingScaleFactor] < 2) {
|
|
uhd = false;
|
|
break;
|
|
}
|
|
}
|
|
SetUHDMode(uhd);
|
|
|
|
Font::SetDefaultFont(StdFont(fceil(DPI([sysfont pointSize]))));
|
|
|
|
GUI_DblClickTime_Write(1000 * NSEvent.doubleClickInterval);
|
|
|
|
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSEventMaskLeftMouseDown)
|
|
handler:^(NSEvent *e) {
|
|
SyncPopupFocus(NULL);
|
|
}];
|
|
[NSEvent addLocalMonitorForEventsMatchingMask:(NSEventMaskLeftMouseDown)
|
|
handler:^NSEvent *(NSEvent *e) {
|
|
SyncPopupFocus([e window]);
|
|
return e;
|
|
}];
|
|
|
|
sClipFmtsRTF = "rtf";
|
|
|
|
Ctrl::ReSkin();
|
|
|
|
EnterGuiMutex();
|
|
}
|
|
|
|
int Ctrl::GetKbdDelay()
|
|
{
|
|
Upp::GuiLock __;
|
|
return int(1000 * NSEvent.keyRepeatDelay);
|
|
}
|
|
|
|
int Ctrl::GetKbdSpeed()
|
|
{
|
|
Upp::GuiLock __;
|
|
return int(1000 * NSEvent.keyRepeatInterval);
|
|
}
|
|
|
|
static NSEvent *current_event;
|
|
|
|
static NSEvent *GetNextEvent(NSDate *until)
|
|
{
|
|
if(!current_event) {
|
|
current_event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
|
untilDate:until
|
|
inMode:NSDefaultRunLoopMode
|
|
dequeue:YES];
|
|
[current_event retain];
|
|
}
|
|
return current_event;
|
|
}
|
|
|
|
static void ReleaseCurrentEvent()
|
|
{
|
|
if(current_event) {
|
|
[current_event release];
|
|
current_event = nil;
|
|
}
|
|
}
|
|
|
|
void CocoExit()
|
|
{
|
|
ReleaseCurrentEvent();
|
|
[main_coco_pool release];
|
|
LeaveGuiMutex();
|
|
}
|
|
|
|
bool Ctrl::IsWaitingEvent()
|
|
{
|
|
return GetNextEvent(nil);
|
|
}
|
|
|
|
bool Ctrl::ProcessEvent(bool *)
|
|
{
|
|
ASSERT(IsMainThread());
|
|
|
|
AutoreleasePool __;
|
|
|
|
ONCELOCK {
|
|
[NSApp finishLaunching];
|
|
}
|
|
|
|
NSEvent *event = GetNextEvent(nil);
|
|
|
|
// DLOG("ProcessEvent " << ToString(event.description));
|
|
|
|
if(!event)
|
|
return false;
|
|
|
|
current_event = nil;
|
|
int n = LeaveGuiMutexAll();
|
|
[NSApp sendEvent:event];
|
|
EnterGuiMutex(n);
|
|
[event release];
|
|
|
|
return true;
|
|
}
|
|
|
|
void SweepMkImageCache();
|
|
|
|
bool Ctrl::ProcessEvents(bool *quit)
|
|
{
|
|
if(ProcessEvent(quit)) {
|
|
while(ProcessEvent(quit) && (!LoopCtrl || LoopCtrl->InLoop()));
|
|
TimerProc(msecs());
|
|
AnimateCaret();
|
|
[NSApp updateWindows];
|
|
SweepMkImageCache();
|
|
return true;
|
|
}
|
|
TimerProc(msecs());
|
|
SweepMkImageCache();
|
|
return false;
|
|
}
|
|
|
|
|
|
void Ctrl::EventLoop(Ctrl *ctrl)
|
|
{
|
|
Upp::GuiLock __;
|
|
ASSERT(IsMainThread());
|
|
ASSERT(LoopLevel == 0 || ctrl);
|
|
LoopLevel++;
|
|
LLOG("Entering event loop at level " << LoopLevel);
|
|
Ptr<Ctrl> ploop;
|
|
if(ctrl) {
|
|
ploop = LoopCtrl;
|
|
LoopCtrl = ctrl;
|
|
ctrl->inloop = true;
|
|
}
|
|
|
|
bool quit = false;
|
|
ProcessEvents(&quit);
|
|
while(ctrl ? ctrl->IsOpen() && ctrl->InLoop() : GetTopCtrls().GetCount())
|
|
{
|
|
// LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": EventLoop / GuiSleep");
|
|
SyncCaret();
|
|
AnimateCaret();
|
|
GuiSleep(20);
|
|
// if(EndSession()) break;
|
|
// LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": EventLoop / ProcessEvents");
|
|
ProcessEvents(&quit);
|
|
// LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": EventLoop / after ProcessEvents");
|
|
}
|
|
|
|
if(ctrl)
|
|
LoopCtrl = ploop;
|
|
LoopLevel--;
|
|
LLOG("Leaving event loop ");
|
|
}
|
|
|
|
static std::atomic<bool> sGuiSleep;
|
|
|
|
void Ctrl::GuiSleep(int ms)
|
|
{
|
|
ASSERT(IsMainThread());
|
|
sGuiSleep = true;
|
|
int n = LeaveGuiMutexAll();
|
|
GetNextEvent([NSDate dateWithTimeIntervalSinceNow:ms / 1000.0]);
|
|
EnterGuiMutex(n);
|
|
sGuiSleep = false;
|
|
}
|
|
|
|
void WakeUpGuiThread(void)
|
|
{
|
|
if(sGuiSleep) {
|
|
sGuiSleep = false;
|
|
[NSApp postEvent:[NSEvent otherEventWithType:NSEventTypeApplicationDefined
|
|
location:NSMakePoint(0, 0)
|
|
modifierFlags:0
|
|
timestamp:0.0
|
|
windowNumber:0
|
|
context:nil
|
|
subtype:0
|
|
data1:0
|
|
data2:0]
|
|
atStart:YES];
|
|
}
|
|
}
|
|
|
|
Rect Ctrl::GetWorkArea() const
|
|
{
|
|
return StdGetWorkArea();
|
|
}
|
|
|
|
Rect MakeScreenRect(NSScreen *screen, CGRect r)
|
|
{
|
|
r.origin.y = [screen frame].size.height - r.origin.y - r.size.height;
|
|
return MakeRect(r, DPI(1));
|
|
}
|
|
|
|
void Ctrl::GetWorkArea(Array<Rect>& rc)
|
|
{
|
|
Upp::GuiLock __;
|
|
for(NSScreen *screen in [NSScreen screens])
|
|
rc.Add(MakeScreenRect(screen, [screen visibleFrame]));
|
|
}
|
|
|
|
|
|
Rect Ctrl::GetVirtualWorkArea()
|
|
{
|
|
Array<Rect> rc;
|
|
GetWorkArea(rc);
|
|
Rect r(0, 0, 0, 0);
|
|
for(int i = 0; i < rc.GetCount(); i++)
|
|
if(i)
|
|
r = r | rc[i];
|
|
else
|
|
r = rc[0];
|
|
return r;
|
|
}
|
|
|
|
Rect Ctrl::GetVirtualScreenArea()
|
|
{
|
|
bool first = true;
|
|
Rect r(0, 0, 0, 0);
|
|
for(NSScreen *screen in [NSScreen screens]) {
|
|
Rect sr = MakeScreenRect(screen, [screen frame]);
|
|
if(first)
|
|
r = sr;
|
|
else
|
|
r = r | sr;
|
|
first = false;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
Rect Ctrl::GetPrimaryWorkArea()
|
|
{
|
|
Array<Rect> rc;
|
|
GetWorkArea(rc);
|
|
return rc.GetCount() ? rc[0] : Rect(0, 0, 0, 0);
|
|
}
|
|
|
|
Rect Ctrl::GetScreenArea(Point pt)
|
|
{
|
|
Upp::GuiLock __;
|
|
for(NSScreen *screen in [NSScreen screens]) {
|
|
Rect rc = MakeScreenRect(screen, [screen frame]);
|
|
if(rc.Contains(pt))
|
|
return rc;
|
|
}
|
|
return GetPrimaryScreenArea();
|
|
}
|
|
|
|
Rect Ctrl::GetPrimaryScreenArea()
|
|
{
|
|
for (NSScreen *screen in [NSScreen screens])
|
|
return MakeScreenRect(screen, [screen frame]);
|
|
return Rect(0, 0, 1024, 768);
|
|
}
|
|
|
|
bool Ctrl::IsCompositedGui()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Rect Ctrl::GetDefaultWindowRect()
|
|
{
|
|
Upp::GuiLock __;
|
|
Rect r = GetPrimaryWorkArea();
|
|
Size sz = r.GetSize();
|
|
|
|
static int pos = min(sz.cx / 10, 50);
|
|
pos += 10;
|
|
int cx = sz.cx * 2 / 3;
|
|
int cy = sz.cy * 2 / 3;
|
|
if(pos + cx + 50 > sz.cx || pos + cy + 50 > sz.cy)
|
|
pos = 0;
|
|
return RectC(r.left + pos + 20, r.top + pos + 20, cx, cy);
|
|
}
|
|
|
|
void Ctrl::GuiPlatformGetTopRect(Rect& r) const
|
|
{
|
|
}
|
|
|
|
void MMCtrl::SyncRect(CocoView *view)
|
|
{
|
|
NSWindow *win = [view window];
|
|
view->ctrl->SetWndRect(MakeScreenRect([win screen], [win contentRectForFrameRect: [win frame]]));
|
|
}
|
|
|
|
TopFrameDraw::TopFrameDraw(Ctrl *ctrl, const Rect& r)
|
|
{
|
|
EnterGuiMutex();
|
|
ctrl = ctrl->GetTopCtrl();
|
|
ASSERT(ctrl->GetTop()->coco);
|
|
Rect tr = ctrl->GetScreenRect();
|
|
NSGraphicsContext *gc = [NSGraphicsContext graphicsContextWithWindow:ctrl->GetTop()->coco->window];
|
|
Init([gc CGContext], NULL);
|
|
|
|
CGContextTranslateCTM(cgHandle, 0, tr.GetHeight());
|
|
CGContextScaleCTM(cgHandle, 1, -1);
|
|
|
|
Clipoff(r);
|
|
}
|
|
|
|
TopFrameDraw::~TopFrameDraw()
|
|
{
|
|
End();
|
|
CGContextFlush(cgHandle);
|
|
LeaveGuiMutex();
|
|
}
|
|
|
|
String GetSpecialDirectory(int i)
|
|
{
|
|
Tuple<int, NSSearchPathDirectory> map[] = {
|
|
{ SF_NSDocumentDirectory, NSDocumentDirectory },
|
|
{ SF_NSUserDirectory, NSUserDirectory },
|
|
{ SF_NSDesktopDirectory, NSDesktopDirectory },
|
|
{ SF_NSDownloadsDirectory, NSDownloadsDirectory },
|
|
{ SF_NSMoviesDirectory, NSMoviesDirectory },
|
|
{ SF_NSMusicDirectory, NSMusicDirectory },
|
|
{ SF_NSPicturesDirectory, NSPicturesDirectory },
|
|
};
|
|
|
|
if(auto *h = FindTuple(map, __countof(map), i)) {
|
|
NSArray * paths = NSSearchPathForDirectoriesInDomains(h->b, NSUserDomainMask, YES);
|
|
if(paths.count)
|
|
return ToString([paths objectAtIndex:0]);
|
|
}
|
|
|
|
return Null;
|
|
};
|
|
|
|
String GetMusicFolder() { return GetSpecialDirectory(SF_NSMusicDirectory); }
|
|
String GetPicturesFolder() { return GetSpecialDirectory(SF_NSPicturesDirectory); }
|
|
String GetVideoFolder() { return GetSpecialDirectory(SF_NSMoviesDirectory); }
|
|
String GetDocumentsFolder() { return GetSpecialDirectory(SF_NSDocumentDirectory); }
|
|
String GetDesktopFolder() { return GetSpecialDirectory(SF_NSDesktopDirectory); }
|
|
String GetTemplatesFolder() { return GetHomeDirectory(); }
|
|
String GetDownloadFolder() { return GetSpecialDirectory(SF_NSDownloadsDirectory); }
|
|
|
|
void CocoBeep()
|
|
{
|
|
NSBeep();
|
|
}
|
|
|
|
extern void (*CocoBeepFn)();
|
|
|
|
INITBLOCK {
|
|
CocoBeepFn = CocoBeep;
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|