mirror of https://github.com/adamdruppe/arsd.git
more mac support, swappable float/int text stuff to experiment with
This commit is contained in:
parent
4ce9cdfd73
commit
2e8da200f9
321
core.d
321
core.d
|
|
@ -26,6 +26,20 @@
|
|||
+/
|
||||
module arsd.core;
|
||||
|
||||
/+
|
||||
Intended to be Supported OSes:
|
||||
* Windows (at least Vista, MAYBE XP)
|
||||
* Linux
|
||||
* FreeBSD 14 (maybe 13 too)
|
||||
* Mac OS
|
||||
|
||||
Eventually also:
|
||||
* ios
|
||||
* OpenBSD
|
||||
* Android
|
||||
* maybe apple watch os?
|
||||
+/
|
||||
|
||||
|
||||
static if(__traits(compiles, () { import core.interpolation; })) {
|
||||
import core.interpolation;
|
||||
|
|
@ -42,6 +56,13 @@ static if(__traits(compiles, () { import core.interpolation; })) {
|
|||
struct InterpolatedExpression(string code) {}
|
||||
}
|
||||
|
||||
// arsd core is now default but you can opt out for a lil while
|
||||
version(no_arsd_core) {
|
||||
|
||||
} else {
|
||||
version=use_arsd_core;
|
||||
}
|
||||
|
||||
version(use_arsd_core)
|
||||
enum use_arsd_core = true;
|
||||
else
|
||||
|
|
@ -127,8 +148,10 @@ import core.time;
|
|||
version(OSXCocoa) {
|
||||
version(ArsdNoCocoa)
|
||||
enum bool UseCocoa = false;
|
||||
else
|
||||
else {
|
||||
version=UseCocoa;
|
||||
enum bool UseCocoa = true;
|
||||
}
|
||||
} else
|
||||
enum bool UseCocoa = false;
|
||||
|
||||
|
|
@ -190,11 +213,11 @@ version(Emscripten) {
|
|||
// THIS FILE DOESN'T ACTUALLY EXIST, WE NEED TO MAKE IT
|
||||
import core.sys.openbsd.sys.event;
|
||||
} else version(OSX) {
|
||||
version=Arsd_core_kqueue;
|
||||
version=Arsd_core_dispatch;
|
||||
|
||||
import core.sys.darwin.sys.event;
|
||||
} else version(iOS) {
|
||||
version=Arsd_core_kqueue;
|
||||
version=Arsd_core_dispatch;
|
||||
|
||||
import core.sys.darwin.sys.event;
|
||||
}
|
||||
|
|
@ -1671,7 +1694,9 @@ inout(char)[] stripRightInternal(return inout(char)[] s) {
|
|||
+/
|
||||
string toStringInternal(T)(T t) {
|
||||
char[32] buffer;
|
||||
static if(is(T : string))
|
||||
static if(is(typeof(t.toString) : string))
|
||||
return t.toString();
|
||||
else static if(is(T : string))
|
||||
return t;
|
||||
else static if(is(T == enum)) {
|
||||
switch(t) {
|
||||
|
|
@ -1693,6 +1718,10 @@ string toStringInternal(T)(T t) {
|
|||
}
|
||||
ret ~= "]";
|
||||
return ret;
|
||||
} else static if(is(T : double)) {
|
||||
import core.stdc.stdio;
|
||||
auto ret = snprintf(buffer.ptr, buffer.length, "%f", t);
|
||||
return buffer[0 .. ret].idup;
|
||||
} else {
|
||||
static assert(0, T.stringof ~ " makes compile too slow");
|
||||
// import std.conv; return to!string(t);
|
||||
|
|
@ -2939,13 +2968,16 @@ interface ICoreEventLoop {
|
|||
1: run before each loop OS wait call
|
||||
2: run after each loop OS wait call
|
||||
3: run both before and after each OS wait call
|
||||
4: single shot?
|
||||
8: no-coalesce? (if after was just run, it will skip the before loops unless this flag is set)
|
||||
4: single shot? NOT IMPLEMENTED
|
||||
8: no-coalesce? NOT IMPLEMENTED (if after was just run, it will skip the before loops unless this flag is set)
|
||||
|
||||
FIXME: it should return a handle you can use to unregister it
|
||||
+/
|
||||
void addDelegateOnLoopIteration(void delegate() dg, uint timingFlags);
|
||||
|
||||
final void addDelegateOnLoopIteration(void function() dg, uint timingFlags) {
|
||||
if(timingFlags == 0)
|
||||
assert(0, "would never run");
|
||||
addDelegateOnLoopIteration(toDelegate(dg), timingFlags);
|
||||
}
|
||||
|
||||
|
|
@ -2969,6 +3001,8 @@ interface ICoreEventLoop {
|
|||
|
||||
version(Arsd_core_epoll) {
|
||||
impl.unregisterFd(fd);
|
||||
} else version(Arsd_core_dispatch) {
|
||||
throw new NotYetImplementedException();
|
||||
} else version(Arsd_core_kqueue) {
|
||||
// intentionally blank - all registrations are one-shot there
|
||||
// FIXME: actually it might not have gone off yet, in that case we do need to delete the filter
|
||||
|
|
@ -2998,6 +3032,8 @@ interface ICoreEventLoop {
|
|||
|
||||
version(Arsd_core_epoll) {
|
||||
impl.unregisterFd(fd);
|
||||
} else version(Arsd_core_dispatch) {
|
||||
throw new NotYetImplementedException();
|
||||
} else version(Arsd_core_kqueue) {
|
||||
// intentionally blank - all registrations are one-shot there
|
||||
// FIXME: actually it might not have gone off yet, in that case we do need to delete the filter
|
||||
|
|
@ -6467,8 +6503,9 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
|
|||
}
|
||||
LoopIterationDelegate[] loopIterationDelegates;
|
||||
|
||||
void runLoopIterationDelegates() {
|
||||
void runLoopIterationDelegates(bool isAfter) {
|
||||
foreach(lid; loopIterationDelegates)
|
||||
if((!isAfter && (lid.flags & 1)) || (isAfter && (lid.flags & 2)))
|
||||
lid.dg();
|
||||
}
|
||||
}
|
||||
|
|
@ -6477,12 +6514,98 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
|
|||
loopIterationDelegates ~= LoopIterationDelegate(dg, timingFlags);
|
||||
}
|
||||
|
||||
version(Arsd_core_dispatch) {
|
||||
|
||||
private NSRunLoop ttrl;
|
||||
|
||||
private this() {
|
||||
ttrl = NSRunLoop.currentRunLoop;
|
||||
}
|
||||
|
||||
// FIXME: this lies!! it runs until completion
|
||||
RunOnceResult runOnce(Duration timeout = Duration.max) {
|
||||
scope(exit) eventLoopRound++;
|
||||
|
||||
// FIXME: autorelease pool
|
||||
|
||||
if(false /*isWorker*/) {
|
||||
runLoopIterationDelegates(false);
|
||||
|
||||
// FIXME: timeout is wrong
|
||||
auto retValue = ttrl.runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture);
|
||||
if(retValue == false)
|
||||
throw new Exception("could not start run loop");
|
||||
|
||||
runLoopIterationDelegates(true);
|
||||
|
||||
// NSApp.run();
|
||||
// exitApplication();
|
||||
//return RunOnceResult(RunOnceResult.Possibilities.GlobalExit);
|
||||
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
|
||||
} else {
|
||||
// ui thread needs to pump nsapp events...
|
||||
runLoopIterationDelegates(false);
|
||||
|
||||
auto timeoutNs = NSDate.distantFuture; // FIXME timeout here, future means no timeout
|
||||
|
||||
again:
|
||||
NSEvent event = NSApp.nextEventMatchingMask(
|
||||
NSEventMask.NSEventMaskAny,
|
||||
timeoutNs,
|
||||
NSDefaultRunLoopMode,
|
||||
true
|
||||
);
|
||||
if(event !is null) {
|
||||
NSApp.sendEvent(event);
|
||||
timeoutNs = NSDate.distantPast; // only keep going if it won't block; we just want to clear the queue
|
||||
goto again;
|
||||
}
|
||||
|
||||
runLoopIterationDelegates(true);
|
||||
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
|
||||
}
|
||||
}
|
||||
|
||||
UnregisterToken addCallbackOnFdReadable(int fd, CallbackHelper cb) {
|
||||
auto input_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());
|
||||
// FIXME: can the GC reap this prematurely?
|
||||
auto b = block(() {
|
||||
cb.call();
|
||||
});
|
||||
// FIXME: should prolly free it eventually idk
|
||||
import core.memory;
|
||||
GC.addRoot(b);
|
||||
|
||||
dispatch_source_set_event_handler(input_src, b);
|
||||
// dispatch_source_set_cancel_handler(input_src, ^{ close(my_file); });
|
||||
dispatch_resume(input_src);
|
||||
|
||||
return UnregisterToken(this, fd, cb);
|
||||
|
||||
}
|
||||
RearmToken addCallbackOnFdReadableOneShot(int fd, CallbackHelper cb) {
|
||||
throw new NotYetImplementedException();
|
||||
}
|
||||
RearmToken addCallbackOnFdWritableOneShot(int fd, CallbackHelper cb) {
|
||||
throw new NotYetImplementedException();
|
||||
}
|
||||
private void rearmFd(RearmToken token) {
|
||||
if(token.readable)
|
||||
cast(void) addCallbackOnFdReadableOneShot(token.fd, token.cb);
|
||||
else
|
||||
cast(void) addCallbackOnFdWritableOneShot(token.fd, token.cb);
|
||||
}
|
||||
}
|
||||
|
||||
version(Arsd_core_kqueue) {
|
||||
// this thread apc dispatches go as a custom event to the queue
|
||||
// the other queues go through one byte at a time pipes (barf). freebsd 13 and newest nbsd have eventfd too tho so maybe i can use them but the other kqueue systems don't.
|
||||
|
||||
RunOnceResult runOnce(Duration timeout = Duration.max) {
|
||||
scope(exit) eventLoopRound++;
|
||||
|
||||
runLoopIterationDelegates(false);
|
||||
|
||||
kevent_t[16] ev;
|
||||
//timespec tout = timespec(1, 0);
|
||||
auto nev = kevent(kqueuefd, null, 0, ev.ptr, ev.length, null/*&tout*/);
|
||||
|
|
@ -6505,7 +6628,7 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
runLoopIterationDelegates();
|
||||
runLoopIterationDelegates(true);
|
||||
|
||||
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
|
||||
}
|
||||
|
|
@ -6652,6 +6775,9 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
|
|||
|
||||
RunOnceResult runOnce(Duration timeout = Duration.max) {
|
||||
scope(exit) eventLoopRound++;
|
||||
|
||||
runLoopIterationDelegates(false);
|
||||
|
||||
if(isWorker) {
|
||||
// this function is only supported on Windows Vista and up, so using this
|
||||
// means dropping support for XP.
|
||||
|
|
@ -6702,7 +6828,7 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
runLoopIterationDelegates();
|
||||
runLoopIterationDelegates(true);
|
||||
|
||||
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
|
||||
}
|
||||
|
|
@ -6988,6 +7114,9 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
|
|||
|
||||
RunOnceResult runOnce(Duration timeout = Duration.max) {
|
||||
scope(exit) eventLoopRound++;
|
||||
|
||||
runLoopIterationDelegates(false);
|
||||
|
||||
epoll_event[16] events;
|
||||
auto ret = epoll_wait(epollfd, events.ptr, cast(int) events.length, -1); // FIXME: timeout
|
||||
if(ret == -1) {
|
||||
|
|
@ -7011,7 +7140,7 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
runLoopIterationDelegates();
|
||||
runLoopIterationDelegates(true);
|
||||
|
||||
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
|
||||
}
|
||||
|
|
@ -8291,7 +8420,7 @@ class LoggerOf(T, size_t bufferSize = 16) {
|
|||
int missedMessages = 0;
|
||||
long n;
|
||||
synchronized(logger) {
|
||||
while(logger.active && connected && logger.writeBufferPosition < readBufferPosition) {
|
||||
while(logger.active && connected && logger.writeBufferPosition <= readBufferPosition) {
|
||||
logger.condition.wait();
|
||||
}
|
||||
|
||||
|
|
@ -8477,6 +8606,16 @@ shared(LoggerOf!GenericEmbeddableInterpolatedSequence) logger() {
|
|||
return _commonLogger;
|
||||
}
|
||||
|
||||
/++
|
||||
Makes note of an exception you catch and otherwise ignore.
|
||||
|
||||
History:
|
||||
Added April 17, 2025
|
||||
+/
|
||||
void logSwallowedException(Exception e) {
|
||||
logger.error(i"$(e.toString())");
|
||||
}
|
||||
|
||||
/+
|
||||
// using this requires a newish compiler so we just uncomment when necessary
|
||||
unittest {
|
||||
|
|
@ -9248,12 +9387,14 @@ package(arsd) version(Windows) extern(Windows) {
|
|||
int WSARecvFrom(SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, sockaddr*, LPINT, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE);
|
||||
}
|
||||
|
||||
package(arsd) static if(UseCocoa) {
|
||||
package(arsd) version(UseCocoa) {
|
||||
|
||||
/* Copy/paste chunk from Jacob Carlborg { */
|
||||
// from https://raw.githubusercontent.com/jacob-carlborg/druntime/550edd0a64f0eb2c4f35d3ec3d88e26b40ac779e/src/core/stdc/clang_block.d
|
||||
// with comments stripped (see docs in the original link), code reformatted, and some names changed to avoid potential conflicts
|
||||
|
||||
// note these should always be passed by pointer!
|
||||
|
||||
import core.stdc.config;
|
||||
struct ObjCBlock(R = void, Params...) {
|
||||
private:
|
||||
|
|
@ -9272,11 +9413,15 @@ private:
|
|||
this.isa = isa;
|
||||
this.flags = flags;
|
||||
this.invoke = invoke;
|
||||
this.dg = dg;
|
||||
this.descriptor = &.objcblock_descriptor;
|
||||
|
||||
// FIXME: is this needed or not? it could be held by the OS and not be visible to GC i think
|
||||
// import core.memory; GC.addRoot(dg.ptr);
|
||||
|
||||
this.dg = dg;
|
||||
}
|
||||
}
|
||||
ObjCBlock!(R, Params) block(R, Params...)(R delegate(Params) dg) {
|
||||
ObjCBlock!(R, Params) blockOnStack(R, Params...)(R delegate(Params) dg) {
|
||||
static if (Params.length == 0)
|
||||
enum flags = 0x50000000;
|
||||
else
|
||||
|
|
@ -9284,6 +9429,14 @@ ObjCBlock!(R, Params) block(R, Params...)(R delegate(Params) dg) {
|
|||
|
||||
return ObjCBlock!(R, Params)(&_NSConcreteStackBlock, flags, &objcblock_invoke!(R, Params), dg);
|
||||
}
|
||||
ObjCBlock!(R, Params)* block(R, Params...)(R delegate(Params) dg) {
|
||||
static if (Params.length == 0)
|
||||
enum flags = 0x50000000;
|
||||
else
|
||||
enum flags = 0x40000000;
|
||||
|
||||
return new ObjCBlock!(R, Params)(&_NSConcreteStackBlock, flags, &objcblock_invoke!(R, Params), dg);
|
||||
}
|
||||
|
||||
private struct Descriptor {
|
||||
c_ulong reserved;
|
||||
|
|
@ -9374,8 +9527,19 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
|
|||
void getCharacters(wchar* buffer, NSRange range) @selector("getCharacters:range:");
|
||||
|
||||
bool getBytes(void* buffer, NSUInteger maxBufferCount, NSUInteger* usedBufferCount, NSStringEncoding encoding, NSStringEncodingConversionOptions options, NSRange range, NSRange* leftover) @selector("getBytes:maxLength:usedLength:encoding:options:range:remainingRange:");
|
||||
|
||||
CGSize sizeWithAttributes(NSDictionary attrs) @selector("sizeWithAttributes:");
|
||||
}
|
||||
|
||||
// FIXME: it is a generic in objc with <KeyType, ObjectType>
|
||||
extern class NSDictionary : NSObject {
|
||||
static NSDictionary dictionaryWithObject(NSObject object, NSid key) @selector("dictionaryWithObject:forKey:");
|
||||
// static NSDictionary initWithObjects(NSArray objects, NSArray forKeys) @selector("initWithObjects:forKeys:");
|
||||
}
|
||||
|
||||
alias NSAttributedStringKey = NSString;
|
||||
/* const */extern __gshared NSAttributedStringKey NSFontAttributeName;
|
||||
|
||||
struct NSRange {
|
||||
NSUInteger loc;
|
||||
NSUInteger len;
|
||||
|
|
@ -9501,7 +9665,42 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
|
|||
|
||||
void run() @selector("run");
|
||||
|
||||
void stop(NSid sender) @selector("stop:");
|
||||
|
||||
void finishLaunching() @selector("finishLaunching");
|
||||
|
||||
void terminate(void*) @selector("terminate:");
|
||||
|
||||
void sendEvent(NSEvent event) @selector("sendEvent:");
|
||||
NSEvent nextEventMatchingMask(
|
||||
NSEventMask mask,
|
||||
NSDate untilDate,
|
||||
NSRunLoopMode inMode,
|
||||
bool dequeue
|
||||
) @selector("nextEventMatchingMask:untilDate:inMode:dequeue:");
|
||||
}
|
||||
|
||||
enum NSEventMask : ulong {
|
||||
NSEventMaskAny = ulong.max
|
||||
}
|
||||
|
||||
version(OSX)
|
||||
extern class NSRunLoop : NSObject {
|
||||
static @property NSRunLoop currentRunLoop() @selector("currentRunLoop");
|
||||
static @property NSRunLoop mainRunLoop() @selector("mainRunLoop");
|
||||
bool runMode(NSRunLoopMode mode, NSDate beforeDate) @selector("runMode:beforeDate:");
|
||||
}
|
||||
|
||||
alias NSRunLoopMode = NSString;
|
||||
|
||||
extern __gshared NSRunLoopMode NSDefaultRunLoopMode;
|
||||
|
||||
version(OSX)
|
||||
extern class NSDate : NSObject {
|
||||
static @property NSDate distantFuture() @selector("distantFuture");
|
||||
static @property NSDate distantPast() @selector("distantPast");
|
||||
static @property NSDate now() @selector("now");
|
||||
|
||||
}
|
||||
|
||||
version(OSX)
|
||||
|
|
@ -9618,6 +9817,7 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
|
|||
NSRect frame() @selector("frame");
|
||||
|
||||
NSRect contentRectForFrameRect(NSRect frameRect) @selector("contentRectForFrameRect:");
|
||||
NSRect frameRectForContentRect(NSRect contentRect) @selector("frameRectForContentRect:");
|
||||
|
||||
NSString title() @selector("title");
|
||||
void title(NSString value) @selector("setTitle:");
|
||||
|
|
@ -9628,6 +9828,8 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
|
|||
void delegate_(NSWindowDelegate) @selector("setDelegate:");
|
||||
|
||||
void setBackgroundColor(NSColor color) @selector("setBackgroundColor:");
|
||||
|
||||
void setIsVisible(bool b) @selector("setIsVisible:");
|
||||
}
|
||||
|
||||
version(OSX)
|
||||
|
|
@ -9844,4 +10046,95 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
|
|||
extern(C) __gshared void* _D4arsd4core6NSView7__ClassZ = null;
|
||||
extern(C) __gshared void* _D4arsd4core8NSWindow7__ClassZ = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern(C) { // grand central dispatch bindings
|
||||
|
||||
// /Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk/usr/include/dispatch
|
||||
// https://swiftlang.github.io/swift-corelibs-libdispatch/tutorial/
|
||||
// https://man.freebsd.org/cgi/man.cgi?query=dispatch_main&sektion=3&apropos=0&manpath=macOS+14.3.1
|
||||
|
||||
struct dispatch_source_type_s {}
|
||||
private __gshared immutable extern {
|
||||
dispatch_source_type_s _dispatch_source_type_timer;
|
||||
dispatch_source_type_s _dispatch_source_type_proc;
|
||||
dispatch_source_type_s _dispatch_source_type_signal;
|
||||
dispatch_source_type_s _dispatch_source_type_read;
|
||||
dispatch_source_type_s _dispatch_source_type_write;
|
||||
dispatch_source_type_s _dispatch_source_type_vnode;
|
||||
// also memory pressure and some others
|
||||
}
|
||||
|
||||
immutable DISPATCH_SOURCE_TYPE_TIMER = &_dispatch_source_type_timer;
|
||||
immutable DISPATCH_SOURCE_TYPE_PROC = &_dispatch_source_type_proc;
|
||||
immutable DISPATCH_SOURCE_TYPE_SIGNAL = &_dispatch_source_type_signal;
|
||||
immutable DISPATCH_SOURCE_TYPE_READ = &_dispatch_source_type_read;
|
||||
immutable DISPATCH_SOURCE_TYPE_WRITE = &_dispatch_source_type_write;
|
||||
immutable DISPATCH_SOURCE_TYPE_VNODE = &_dispatch_source_type_vnode;
|
||||
// also are some for internal data change things and a couple others
|
||||
|
||||
enum DISPATCH_PROC_EXIT = 0x80000000; // process exited
|
||||
enum DISPATCH_PROC_FORK = 0x40000000; // it forked
|
||||
enum DISPATCH_PROC_EXEC = 0x20000000; // it execed
|
||||
enum DISPATCH_PROC_SIGNAL = 0x08000000; // it received a signal
|
||||
|
||||
enum DISPATCH_VNODE_DELETE = 0x1;
|
||||
enum DISPATCH_VNODE_WRITE = 0x2;
|
||||
enum DISPATCH_VNODE_EXTEND = 0x4;
|
||||
enum DISPATCH_VNODE_ATTRIB = 0x8;
|
||||
enum DISPATCH_VNODE_LINK = 0x10;
|
||||
enum DISPATCH_VNODE_RENAME = 0x20;
|
||||
enum DISPATCH_VNODE_REVOKE = 0x40;
|
||||
enum DISPATCH_VNODE_FUNLOCK = 0x100;
|
||||
|
||||
private struct dispatch_source_s;
|
||||
private struct dispatch_queue_s {}
|
||||
|
||||
alias dispatch_source_type_t = const(dispatch_source_type_s)*;
|
||||
|
||||
alias dispatch_source_t = dispatch_source_s*; // NSObject<OS_dispatch_source>
|
||||
alias dispatch_queue_t = dispatch_queue_s*; // NSObject<OS_dispatch_queue>
|
||||
alias dispatch_object_t = void*; // actually a "transparent union" of the dispatch_source_t, dispatch_queue_t, and others
|
||||
alias dispatch_block_t = ObjCBlock!(void)*;
|
||||
static if(void*.sizeof == 8)
|
||||
alias uintptr_t = ulong;
|
||||
else
|
||||
alias uintptr_t = uint;
|
||||
|
||||
dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, c_ulong mask, dispatch_queue_t queue);
|
||||
void dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t handler);
|
||||
void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t handler);
|
||||
void dispatch_source_cancel(dispatch_source_t source);
|
||||
|
||||
// DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial);
|
||||
// dispatch_queue_t dispatch_get_main_queue();
|
||||
|
||||
extern __gshared dispatch_queue_s _dispatch_main_q;
|
||||
|
||||
extern(D) dispatch_queue_t dispatch_get_main_queue() {
|
||||
return &_dispatch_main_q;
|
||||
}
|
||||
|
||||
// FIXME: what is dispatch_time_t ???
|
||||
// dispatch_time
|
||||
// dispatch_walltime
|
||||
|
||||
// void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, ulong interval, ulong leeway);
|
||||
|
||||
void dispatch_retain(dispatch_object_t object);
|
||||
void dispatch_release(dispatch_object_t object);
|
||||
|
||||
void dispatch_resume(dispatch_object_t object);
|
||||
void dispatch_pause(dispatch_object_t object);
|
||||
|
||||
void* dispatch_get_context(dispatch_object_t object);
|
||||
void dispatch_set_context(dispatch_object_t object, void* context);
|
||||
|
||||
// sends a function to the given queue
|
||||
void dispatch_sync(dispatch_queue_t queue, scope dispatch_block_t block);
|
||||
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
|
||||
|
||||
} // grand central dispatch bindings
|
||||
|
||||
}
|
||||
|
|
|
|||
29
minigui.d
29
minigui.d
|
|
@ -1007,7 +1007,7 @@ class Widget : ReflectableProperties {
|
|||
protected final int defaultLineHeight() {
|
||||
auto cs = getComputedStyle();
|
||||
if(cs.font && !cs.font.isNull)
|
||||
return cs.font.height() * 5 / 4;
|
||||
return castFnumToCnum(cs.font.height() * 5 / 4);
|
||||
else
|
||||
return scaleWithDpi(Window.lineHeightNotDeprecatedButShouldBeSinceItIsJustAFallback * 5/4);
|
||||
}
|
||||
|
|
@ -1020,7 +1020,7 @@ class Widget : ReflectableProperties {
|
|||
protected final int defaultTextHeight(int numberOfLines = 1) {
|
||||
auto cs = getComputedStyle();
|
||||
if(cs.font && !cs.font.isNull)
|
||||
return cs.font.height() * numberOfLines;
|
||||
return castFnumToCnum(cs.font.height() * numberOfLines);
|
||||
else
|
||||
return Window.lineHeightNotDeprecatedButShouldBeSinceItIsJustAFallback * numberOfLines;
|
||||
}
|
||||
|
|
@ -1028,7 +1028,7 @@ class Widget : ReflectableProperties {
|
|||
protected final int defaultTextWidth(const(char)[] text) {
|
||||
auto cs = getComputedStyle();
|
||||
if(cs.font && !cs.font.isNull)
|
||||
return cs.font.stringWidth(text);
|
||||
return castFnumToCnum(cs.font.stringWidth(text));
|
||||
else
|
||||
return scaleWithDpi(Window.lineHeightNotDeprecatedButShouldBeSinceItIsJustAFallback * cast(int) text.length / 2);
|
||||
}
|
||||
|
|
@ -1875,7 +1875,10 @@ class Widget : ReflectableProperties {
|
|||
x = pt.x;
|
||||
y = pt.y;
|
||||
} else {
|
||||
featureNotImplemented();
|
||||
auto rect = this.parentWindow.win.impl.window.frame;
|
||||
// FIXME: confirm?
|
||||
x += cast(int) rect.origin.x;
|
||||
y += cast(int) rect.origin.y;
|
||||
}
|
||||
|
||||
return Point(x, y);
|
||||
|
|
@ -9261,12 +9264,12 @@ class Window : Widget {
|
|||
if(defaultHeightCache == 0) {
|
||||
font = new OperatingSystemFont;
|
||||
font.loadDefault;
|
||||
defaultHeightCache = font.height();// * 5 / 4;
|
||||
defaultHeightCache = castFnumToCnum(font.height());// * 5 / 4;
|
||||
}
|
||||
return defaultHeightCache;
|
||||
}
|
||||
|
||||
return font.height();// * 5 / 4;
|
||||
return castFnumToCnum(font.height());// * 5 / 4;
|
||||
}
|
||||
|
||||
Widget focusedWidget;
|
||||
|
|
@ -12221,7 +12224,7 @@ class StatusBar : Widget {
|
|||
auto cs = getComputedStyle();
|
||||
auto font = cs.font;
|
||||
|
||||
part.currentlyAssignedWidth = font.averageWidth * this.width;
|
||||
part.currentlyAssignedWidth = castFnumToCnum(font.averageWidth * this.width);
|
||||
remainingLength -= part.currentlyAssignedWidth;
|
||||
break;
|
||||
case Proportional:
|
||||
|
|
@ -14642,12 +14645,12 @@ class TextDisplayHelper : Widget {
|
|||
}
|
||||
|
||||
bool isMonospace() { return false; }
|
||||
int averageWidth() { return image_.width; }
|
||||
int height() { return image_.height; }
|
||||
int ascent() { return image_.height; }
|
||||
int descent() { return 0; }
|
||||
fnum averageWidth() { return image_.width; }
|
||||
fnum height() { return image_.height; }
|
||||
fnum ascent() { return image_.height; }
|
||||
fnum descent() { return 0; }
|
||||
|
||||
int stringWidth(scope const(char)[] s, SimpleWindow window = null) {
|
||||
fnum stringWidth(scope const(char)[] s, SimpleWindow window = null) {
|
||||
return image_.width;
|
||||
}
|
||||
|
||||
|
|
@ -15083,7 +15086,7 @@ class PasswordEdit : EditableTextWidget {
|
|||
this() {
|
||||
super(cs.font);
|
||||
}
|
||||
override int stringWidth(scope const(char)[] text, SimpleWindow window = null) {
|
||||
override fnum stringWidth(scope const(char)[] text, SimpleWindow window = null) {
|
||||
int count = 0;
|
||||
foreach(dchar ch; text)
|
||||
count++;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ class TerminalEmulatorWidget : Widget {
|
|||
super(parent);
|
||||
}
|
||||
|
||||
mixin Observable!(MemoryImage, "icon"); // please note it can be changed to null!
|
||||
mixin Observable!(string, "title");
|
||||
|
||||
this(string[] args, Widget parent) {
|
||||
version(Windows) {
|
||||
import core.sys.windows.windows : HANDLE;
|
||||
|
|
@ -102,24 +105,39 @@ class TerminalEmulatorInsideWidget : TerminalEmulator {
|
|||
protected override void changeCursorStyle(CursorStyle s) { }
|
||||
|
||||
protected override void changeWindowTitle(string t) {
|
||||
//if(window && t.length)
|
||||
//window.title = t;
|
||||
widget.title = t;
|
||||
}
|
||||
|
||||
// FIXME: minigui TabWidget ought to be able to accept icons too.
|
||||
protected override void changeWindowIcon(IndexedImage t) {
|
||||
//if(window && t)
|
||||
//window.icon = t;
|
||||
widget.icon = t;
|
||||
}
|
||||
protected override void changeIconTitle(string) {}
|
||||
protected override void changeTextAttributes(TextAttributes) {}
|
||||
|
||||
// FIXME: should we be able to delegate this up the chain too?
|
||||
protected override void soundBell() {
|
||||
static if(UsingSimpledisplayX11)
|
||||
XBell(XDisplayConnection.get(), 50);
|
||||
}
|
||||
|
||||
protected override void demandAttention() {
|
||||
//window.requestAttention();
|
||||
// to trigger: echo -e '\033]5001;1\007'
|
||||
|
||||
widget.emitCommand!"requestAttention";
|
||||
|
||||
// to acknowledge:
|
||||
// attentionReceived();
|
||||
}
|
||||
|
||||
override void requestExit() {
|
||||
sdpyPrintDebugString("exit");
|
||||
widget.emitCommand!"requestExit";
|
||||
// FIXME
|
||||
}
|
||||
|
||||
|
||||
protected override void changeIconTitle(string) {}
|
||||
protected override void changeTextAttributes(TextAttributes) {}
|
||||
|
||||
protected override void copyToClipboard(string text) {
|
||||
setClipboardText(widget.parentWindow.win, text);
|
||||
}
|
||||
|
|
@ -149,10 +167,6 @@ class TerminalEmulatorInsideWidget : TerminalEmulator {
|
|||
getPrimarySelection(widget.parentWindow.win, dg);
|
||||
}
|
||||
|
||||
override void requestExit() {
|
||||
// FIXME
|
||||
}
|
||||
|
||||
|
||||
|
||||
void resizeImage() { }
|
||||
|
|
|
|||
1553
simpledisplay.d
1553
simpledisplay.d
File diff suppressed because it is too large
Load Diff
|
|
@ -4542,6 +4542,11 @@ mixin template SdpyDraw() {
|
|||
this.font = new OperatingSystemFont("Courier New", size, FontWeight.medium);
|
||||
if(!this.font.isNull && !this.font.isMonospace)
|
||||
this.font.unload(); // non-monospace fonts are unusable here. This should never happen anyway though as Courier New comes with Windows
|
||||
} else version(OSX) {
|
||||
this.font = new OperatingSystemFont("Courier New", size, FontWeight.medium);
|
||||
if(!this.font.isNull && !this.font.isMonospace)
|
||||
throw new Exception("non monospace");
|
||||
//this.font.unload();
|
||||
}
|
||||
|
||||
if(font.isNull) {
|
||||
|
|
@ -4549,8 +4554,9 @@ mixin template SdpyDraw() {
|
|||
fontWidth = size / 2;
|
||||
fontHeight = size;
|
||||
} else {
|
||||
fontWidth = font.averageWidth;
|
||||
fontHeight = font.height;
|
||||
fontWidth = cast(int) font.averageWidth;
|
||||
fontHeight = cast(int) font.height;
|
||||
// import std.stdio; writeln(fontWidth, " x ", fontHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ import arsd.simpledisplay;
|
|||
|
||||
// You can do the caret by any time it gets drawn, you set the flag that it is on, then you can xor it to turn it off and keep track of that at top level.
|
||||
|
||||
alias width_t = float;// short;
|
||||
|
||||
|
||||
// FIXME: might want to be able to swap out all styles at once and trigger whole relayout, as if a document theme changed wholesale, without changing the saved style handles
|
||||
// FIXME: line and paragrpah numbering options while drawing
|
||||
|
|
@ -227,13 +229,13 @@ class TerminalFontRepresentation : MeasurableFont {
|
|||
}
|
||||
|
||||
bool isMonospace() { return true; }
|
||||
int averageWidth() { return 1; }
|
||||
int height() { return 1; }
|
||||
fnum averageWidth() { return 1; }
|
||||
fnum height() { return 1; }
|
||||
/// since it is a grid this is a bit bizarre to translate.
|
||||
int ascent() { return 1; }
|
||||
int descent() { return 0; }
|
||||
fnum ascent() { return 1; }
|
||||
fnum descent() { return 0; }
|
||||
|
||||
int stringWidth(scope const(char)[] s, SimpleWindow window = null) {
|
||||
fnum stringWidth(scope const(char)[] s, SimpleWindow window = null) {
|
||||
int count;
|
||||
foreach(dchar ch; s)
|
||||
count++;
|
||||
|
|
@ -1424,7 +1426,7 @@ class TextLayouter {
|
|||
auto w = segmentsWidths[segmentIndex][codepointCounter];
|
||||
|
||||
if(thing + segment.textBeginOffset == idx) {
|
||||
bb.right = bb.left + w;
|
||||
bb.right = cast(typeof(bb.right))(bb.left + w);
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
|
@ -1511,7 +1513,7 @@ class TextLayouter {
|
|||
// the rest of this might need splitting based on selections
|
||||
|
||||
DrawingInformation di;
|
||||
di.boundingBox = Rectangle(segment.upperLeft, Size(segment.width, segment.height));
|
||||
di.boundingBox = Rectangle(segment.upperLeft, Size(castFnumToCnum(segment.width), segment.height));
|
||||
di.selections = 0;
|
||||
|
||||
// di.initialBaseline = Point(x, y); // FIXME
|
||||
|
|
@ -1650,7 +1652,7 @@ class TextLayouter {
|
|||
|
||||
// the layout function calculates these
|
||||
Segment[] segments;
|
||||
short[][] segmentsWidths;
|
||||
width_t[][] segmentsWidths;
|
||||
}
|
||||
|
||||
/++
|
||||
|
|
@ -1715,7 +1717,7 @@ class TextLayouter {
|
|||
int styleInformationIndex;
|
||||
|
||||
// calculated values after iterating through the segment
|
||||
int width; // short
|
||||
MeasurableFont.fnum width; // short
|
||||
int height; // short
|
||||
|
||||
Point upperLeft;
|
||||
|
|
@ -1729,7 +1731,7 @@ class TextLayouter {
|
|||
*/
|
||||
|
||||
Rectangle boundingBox() {
|
||||
return Rectangle(upperLeft, Size(width, height));
|
||||
return Rectangle(upperLeft, Size(castFnumToCnum(width), height));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1776,7 +1778,7 @@ class TextLayouter {
|
|||
idx = segment.textBeginOffset;
|
||||
// FIXME: this all assumes ltr
|
||||
|
||||
auto boundingBox = Rectangle(segment.upperLeft, Size(segment.width, segment.height));
|
||||
auto boundingBox = Rectangle(segment.upperLeft, Size(castFnumToCnum(segment.width), segment.height));
|
||||
if(boundingBox.contains(p)) {
|
||||
int x = segment.upperLeft.x;
|
||||
int codePointIndex = 0;
|
||||
|
|
@ -2118,11 +2120,14 @@ class TextLayouter {
|
|||
Segment segment;
|
||||
|
||||
Segment previousOldSavedSegment;
|
||||
short[] previousOldSavedWidths;
|
||||
width_t[] previousOldSavedWidths;
|
||||
TextStyle currentStyle = null;
|
||||
int currentStyleIndex = 0;
|
||||
MeasurableFont font;
|
||||
bool glyphCacheValid;
|
||||
version(OSX)
|
||||
float[128] glyphWidths;
|
||||
else
|
||||
ubyte[128] glyphWidths;
|
||||
void loadNewFont(MeasurableFont what) {
|
||||
font = what;
|
||||
|
|
@ -2135,6 +2140,9 @@ class TextLayouter {
|
|||
glyphCacheValid = false;
|
||||
break;
|
||||
}
|
||||
version(OSX)
|
||||
glyphWidths[c] = w;
|
||||
else
|
||||
glyphWidths[c] = cast(ubyte) w; // FIXME: what if it doesn't fit?
|
||||
}
|
||||
}
|
||||
|
|
@ -2164,7 +2172,7 @@ class TextLayouter {
|
|||
|
||||
assert(offsetToNextStyle >= 0);
|
||||
|
||||
short[] widths;
|
||||
width_t[] widths;
|
||||
|
||||
size_t segmentBegan = invalidStart;
|
||||
void finishSegment(size_t idx) {
|
||||
|
|
@ -2193,8 +2201,8 @@ class TextLayouter {
|
|||
}
|
||||
|
||||
// FIXME: when we start in an invalidated thing this is not necessarily right, it should be calculated above
|
||||
int biggestDescent = font.descent;
|
||||
int lineHeight = font.height;
|
||||
auto biggestDescent = font.descent;
|
||||
auto lineHeight = font.height;
|
||||
|
||||
bool finishLine(size_t idx, MeasurableFont outerFont) {
|
||||
if(segment.textBeginOffset == idx)
|
||||
|
|
@ -2227,7 +2235,7 @@ class TextLayouter {
|
|||
auto baseline = thisLineHeight - biggestDescent;
|
||||
|
||||
seg.upperLeft.y += baseline - font.ascent;
|
||||
seg.height = thisLineHeight - (baseline - font.ascent);
|
||||
seg.height = castFnumToCnum(thisLineHeight - (baseline - font.ascent));
|
||||
}
|
||||
|
||||
// now need to check if we can finish relayout early
|
||||
|
|
@ -2375,7 +2383,7 @@ class TextLayouter {
|
|||
|
||||
|
||||
|
||||
int thisWidth = 0;
|
||||
MeasurableFont.fnum thisWidth = 0;
|
||||
|
||||
// FIXME: delegate private-use area to their own segments
|
||||
// FIXME: line separator, paragraph separator, form feed
|
||||
|
|
@ -2456,7 +2464,7 @@ class TextLayouter {
|
|||
|
||||
advance:
|
||||
if(segment.textBeginOffset != -1) {
|
||||
widths ~= cast(short) thisWidth;
|
||||
widths ~= cast(width_t) thisWidth;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue