From 4a913bb7fb26d6e448cb5ce6bc0e31f3bb1a36e8 Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Sun, 7 Sep 2025 10:30:49 -0400 Subject: [PATCH] opend chnging some defaults - char, float, monitor, preparing arsd --- cgi.d | 24 ++++++++++++++++++++++-- core.d | 20 +++++++++++++++++--- minigui.d | 2 +- simpleaudio.d | 22 +++++++++++----------- simpledisplay.d | 9 +++++---- terminal.d | 39 +++++++++++++++++++++++---------------- 6 files changed, 79 insertions(+), 37 deletions(-) diff --git a/cgi.d b/cgi.d index d1d2ad1..03ca2e7 100644 --- a/cgi.d +++ b/cgi.d @@ -606,6 +606,8 @@ unittest { static import std.file; static import arsd.core; +import arsd.core : EnableSynchronization; // polyfill for opend with removed monitor + version(Posix) import arsd.core : makeNonBlocking; @@ -1883,7 +1885,7 @@ class Cgi { // not using maxContentLength because that might be cranked up to allow // large file uploads. We can handle them, but a huge post[] isn't any good. - if(pps.buffer.length + chunk.length > 8 * 1024 * 1024) // surely this is plenty big enough + if(pps.buffer.length + chunk.length > 24 * 1024 * 1024) // surely this is plenty big enough throw new Exception("wtf is up with such a gigantic form submission????"); pps.buffer ~= chunk; @@ -2289,6 +2291,9 @@ class Cgi { ``` To ensure the necessary data is available to cgi.d. + + History: + The overload with the `checker` callback was added July 29, 2025. +/ void requireBasicAuth(string user, string pass, string message = null, string file = __FILE__, size_t line = __LINE__) { if(authorization != "Basic " ~ Base64.encode(cast(immutable(ubyte)[]) (user ~ ":" ~ pass))) { @@ -2296,6 +2301,17 @@ class Cgi { } } + /// ditto + void requireBasicAuth(scope bool delegate(string user, string pass) checker, string message = null, string file = __FILE__, size_t line = __LINE__) { + // FIXME + /+ + if(authorization != "Basic " ~ Base64.encode(cast(immutable(ubyte)[]) (user ~ ":" ~ pass))) { + throw new AuthorizationRequiredException("Basic", message, file, line); + } + +/ + } + + /// Very simple caching controls - setCache(false) means it will never be cached. Good for rapidly updated or sensitive sites. /// setCache(true) means it will always be cached for as long as possible. Best for static content. /// Use setResponseExpires and updateResponseExpires for more control @@ -5959,6 +5975,8 @@ import core.atomic; FIXME: should I offer an event based async thing like netman did too? Yeah, probably. */ class ListeningConnectionManager { + version(D_OpenD) mixin EnableSynchronization; + Semaphore semaphore; Socket[256] queue; shared(ubyte) nextIndexFront; @@ -12130,8 +12148,10 @@ auto handleWith(alias handler)(string urlPrefix) { // cuz I'm too lazy to do it better right now static class Hack : WebObject { static import std.traits; + static if(is(typeof(handler) Params == __parameters)) + @(__traits(getAttributes, handler)) @UrlName("") - auto handle(std.traits.Parameters!handler args) { + auto handle(Params args) { return handler(args); } } diff --git a/core.d b/core.d index 5fec19f..7c6f0aa 100644 --- a/core.d +++ b/core.d @@ -56,6 +56,18 @@ static if(__traits(compiles, () { import core.interpolation; })) { struct InterpolatedExpression(string code) {} } +static if(!__traits(hasMember, object, "SynchronizableObject")) { + alias SynchronizableObject = Object; + mixin template EnableSynchronization() {} +} else { + alias EnableSynchronization = Object.EnableSynchronization; +} + +// the old char.inits if you need them +enum char char_invalid = '\xFF'; +enum wchar wchar_invalid = '\uFFFF'; +enum dchar dchar_invalid = '\U0000FFFF'; + // arsd core is now default but you can opt out for a lil while version(no_arsd_core) { @@ -7152,7 +7164,7 @@ private class CoreEventLoopImplementation : ICoreEventLoop { } - private static final class CallbackQueue { + private static final class CallbackQueue : SynchronizableObject { int fd = -1; string name; CallbackHelper callback; @@ -7489,14 +7501,14 @@ struct SynchronizedCircularBuffer(T, size_t maxSize = 128) { private int front; private int back; - private Object synchronizedOn; + private SynchronizableObject synchronizedOn; @disable this(); /++ The Object's monitor is used to synchronize the methods in here. +/ - this(Object synchronizedOn) { + this(SynchronizableObject synchronizedOn) { this.synchronizedOn = synchronizedOn; } @@ -8481,6 +8493,8 @@ unittest { Not actually implemented until February 6, 2025, when it changed from mixin template to class. +/ class LoggerOf(T, size_t bufferSize = 16) { + version(D_OpenD) mixin EnableSynchronization; + private LoggedMessage!T[bufferSize] ring; private ulong writeBufferPosition; diff --git a/minigui.d b/minigui.d index 48526cb..7a4dda5 100644 --- a/minigui.d +++ b/minigui.d @@ -16385,7 +16385,7 @@ class Event : ReflectableProperties { dchar character() { if(auto ce = cast(CharEvent) this) return ce.character; - return dchar.init; + return dchar_invalid; } } diff --git a/simpleaudio.d b/simpleaudio.d index 9e78ef7..5717468 100644 --- a/simpleaudio.d +++ b/simpleaudio.d @@ -367,13 +367,13 @@ class DummySample : SampleController { void pause() {} void resume() {} void stop() {} - float position() { return float.init; } + float position() { return float.nan; } bool finished() { return true; } bool paused() { return true; } - float duration() { return float.init; } + float duration() { return float.nan; } float volume() { return 1.0; } - float volume(float v) { return float.init; } + float volume(float v) { return float.nan; } float playbackSpeed() { return 1.0; } void playbackSpeed(float v) { } @@ -397,18 +397,18 @@ private final class SampleControlFlags : SampleController { void seek(float where) { synchronized(this) {if(where < 0) where = 0; requestedSeek = where;} } float currentPosition = 0.0; - float requestedSeek = float.init; + float requestedSeek = float.nan; - float detectedDuration; + float detectedDuration = float.nan; float duration() { return detectedDuration; } // FIXME: these aren't implemented float volume() { return 1.0; } - float volume(float v) { return float.init; } + float volume(float v) { return float.nan; } float playbackSpeed_ = 1.0; - float requestedPlaybackSpeed; + float requestedPlaybackSpeed = float.nan; float playbackSpeed() { return playbackSpeed_; } void playbackSpeed(float v) { requestedPlaybackSpeed = v; } @@ -420,18 +420,18 @@ private final class SampleControlFlags : SampleController { ) { // should I synchronize it after all? synchronized(this) { - if(this.requestedSeek !is float.init) { + if(this.requestedSeek !is float.nan) { if(executeSeek !is null && executeSeek(this.requestedSeek)) { this.currentPosition = this.requestedSeek; } - this.requestedSeek = float.init; + this.requestedSeek = float.nan; } - if(this.requestedPlaybackSpeed !is float.init) { + if(this.requestedPlaybackSpeed !is float.nan) { if(executePlaybackSpeed !is null && executePlaybackSpeed(this.playbackSpeed_)) { this.playbackSpeed_ = this.requestedPlaybackSpeed; } - this.requestedPlaybackSpeed = float.init; + this.requestedPlaybackSpeed = float.nan; } } diff --git a/simpledisplay.d b/simpledisplay.d index d68b202..71e78e2 100644 --- a/simpledisplay.d +++ b/simpledisplay.d @@ -1784,7 +1784,7 @@ float[2] getDpi() { } auto xft = getXftDpi(); - if(xft is float.init) + if(xft is float.nan) fallback(); else { dpi[0] = xft; @@ -1814,7 +1814,7 @@ float getXftDpi() { } } - return float.init; + return float.nan; } /++ @@ -2012,6 +2012,7 @@ enum CornerStyle { will need to destroy it yourself. +/ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon { + version(D_OpenD) mixin EnableSynchronization; /++ Copies the window's current state into a [TrueColorImage]. @@ -4315,7 +4316,7 @@ struct EventLoop { EventLoop.get().exit(); } - private __gshared static Object monitor = new Object(); // deliberate CTFE usage here fyi + private __gshared static SynchronizableObject monitor = new SynchronizableObject(); // deliberate CTFE usage here fyi /// Construct an application-global event loop for yourself /// See_Also: [SimpleWindow.setEventHandlers] @@ -10980,7 +10981,7 @@ private struct RunQueueMember { } private __gshared RunQueueMember*[] runInGuiThreadQueue; -private __gshared Object runInGuiThreadLock = new Object; // intentional CTFE +private __gshared SynchronizableObject runInGuiThreadLock = new SynchronizableObject; // intentional CTFE private bool thisIsGuiThread = false; private shared bool guiThreadExists_ = false; private shared bool guiThreadTerminating = false; diff --git a/terminal.d b/terminal.d index 9fd2bcc..5fff6c0 100644 --- a/terminal.d +++ b/terminal.d @@ -269,6 +269,8 @@ __gshared void delegate() nothrow @nogc sigIntExtension; static import arsd.core; +public import arsd.core : dchar_invalid; + import core.stdc.stdio; version(TerminalDirectToEmulator) { @@ -2543,7 +2545,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as Params: prompt = the prompt to give the user. For example, `"Your name: "`. - echoChar = the character to show back to the user as they type. The default value of `dchar.init` shows the user their own input back normally. Passing `0` here will disable echo entirely, like a Unix password prompt. Or you might also try `'*'` to do a password prompt that shows the number of characters input to the user. + echoChar = the character to show back to the user as they type. The default value of `dchar_invalid` shows the user their own input back normally. Passing `0` here will disable echo entirely, like a Unix password prompt. Or you might also try `'*'` to do a password prompt that shows the number of characters input to the user. prefilledData = the initial data to populate the edit buffer History: @@ -2557,7 +2559,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as On November 7, 2023 (dub v11.3), this function started returning stdin.readln in the event that the instance is not connected to a terminal. +/ - string getline(string prompt = null, dchar echoChar = dchar.init, string prefilledData = null) { + string getline(string prompt = null, dchar echoChar = dchar_invalid, string prefilledData = null) { if(!usingDirectEmulator && type != ConsoleOutputType.minimalProcessing) if(!stdoutIsTerminal || !stdinIsTerminal) { import std.stdio; @@ -2601,7 +2603,7 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as } /// ditto - string getline(string prompt, string prefilledData, dchar echoChar = dchar.init) { + string getline(string prompt, string prefilledData, dchar echoChar = dchar_invalid) { return getline(prompt, echoChar, prefilledData); } @@ -2697,14 +2699,17 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as throw new Exception("terminal reply timed out"); auto len = read(terminal.fdIn, buffer.ptr, buffer.length); if(len == -1) { - if(errno == EINTR) + if(errno == EINTR) { + tries++; goto try_again; + } if(errno == EAGAIN || errno == EWOULDBLOCK) { import core.thread; Thread.sleep(10.msecs); tries++; goto try_again; } + throw new Exception("Other error in read cursor position"); } else if(len == 0) { throw new Exception("Couldn't get cursor position to initialize get line " ~ to!string(len) ~ " " ~ to!string(errno)); } @@ -3304,7 +3309,7 @@ struct RealTimeConsoleInput { bool kbhit() { auto got = getch(true); - if(got == dchar.init) + if(got == dchar_invalid) return false; getchBuffer = got; @@ -3385,21 +3390,21 @@ struct RealTimeConsoleInput { } } - private dchar getchBuffer; + private dchar getchBuffer = dchar_invalid; /// Get one key press from the terminal, discarding other - /// events in the process. Returns dchar.init upon receiving end-of-file. + /// events in the process. Returns dchar_invalid upon receiving end-of-file. /// /// Be aware that this may return non-character key events, like F1, F2, arrow keys, etc., as private use Unicode characters. Check them against KeyboardEvent.Key if you like. dchar getch(bool nonblocking = false) { - if(getchBuffer != dchar.init) { + if(getchBuffer != dchar_invalid) { auto a = getchBuffer; - getchBuffer = dchar.init; + getchBuffer = dchar_invalid; return a; } if(nonblocking && !anyInput_internal()) - return dchar.init; + return dchar_invalid; auto event = nextEvent(); while(event.type != InputEvent.Type.KeyboardEvent || event.keyboardEvent.pressed == false) { @@ -3408,10 +3413,10 @@ struct RealTimeConsoleInput { if(event.type == InputEvent.Type.HangupEvent) throw new HangupException(); if(event.type == InputEvent.Type.EndOfFileEvent) - return dchar.init; + return dchar_invalid; if(nonblocking && !anyInput_internal()) - return dchar.init; + return dchar_invalid; event = nextEvent(); } @@ -6190,7 +6195,7 @@ class LineGetter { Possible values are: - `dchar.init` = normal; user can see their input. + `dchar_invalid` = normal; user can see their input. `'\0'` = nothing; the cursor does not visually move as they edit. Similar to Unix style password prompts. @@ -6198,8 +6203,10 @@ class LineGetter { History: Added October 11, 2021 (dub v10.4) + + OpenD changed `dchar.init` from an invalid char to `0` in September 2025. If you explicitly assigned `dchar.init`, I strongly recommend changing that to `dchar_invalid` for maximum compatibility. +/ - dchar echoChar = dchar.init; + dchar echoChar = dchar_invalid; protected static struct Drawer { LineGetter lg; @@ -6248,7 +6255,7 @@ class LineGetter { if(lg.echoChar == '\0') return; - else if(lg.echoChar !is dchar.init) + else if(lg.echoChar !is dchar_invalid) ch = lg.echoChar; auto l = encode(buffer, ch); @@ -9439,7 +9446,7 @@ version(TerminalDirectToEmulator) { fontSize = widget.scaleWithDpi(fontSize); } else { auto xft = getXftDpi(); - if(xft is float.init) + if(xft is float.nan) xft = 96; // the xft passed as assumed means it will figure that's what the size // is based on (which it is, inside xft) preventing the double scale problem