mirror of https://github.com/adamdruppe/arsd.git
new blocking control param
This commit is contained in:
parent
39d859a21d
commit
1ed17bdb43
21
minigui.d
21
minigui.d
|
@ -7541,11 +7541,24 @@ class Window : Widget {
|
|||
return true;
|
||||
}
|
||||
|
||||
/// Shows the window and runs the application event loop.
|
||||
/++
|
||||
Shows the window and runs the application event loop.
|
||||
|
||||
Blocks until this window is closed.
|
||||
|
||||
History:
|
||||
The [BlockingMode] parameter was added on December 8, 2021.
|
||||
The default behavior is to block until the application quits
|
||||
(so all windows have been closed), unless another minigui or
|
||||
simpledisplay event loop is already running, in which case it
|
||||
will block until this window closes specifically.
|
||||
+/
|
||||
@scriptable
|
||||
void loop() {
|
||||
void loop(BlockingMode bm = BlockingMode.automatic) {
|
||||
if(win.closed)
|
||||
return; // otherwise show will throw
|
||||
show();
|
||||
win.eventLoop(0);
|
||||
win.eventLoopWithBlockingMode(bm, 0);
|
||||
}
|
||||
|
||||
private bool firstShow = true;
|
||||
|
@ -12558,7 +12571,7 @@ void getFileName(
|
|||
bool openOrSave,
|
||||
void delegate(string) onOK,
|
||||
string prefilledName = null,
|
||||
string[] filters = null, // format here is like ["Text files\0*.txt;*.text", "Image files\n*.png;*.jpg"]
|
||||
string[] filters = null, // format here is like ["Text files\0*.txt;*.text", "Image files\0*.png;*.jpg"]
|
||||
void delegate() onCancel = null,
|
||||
)
|
||||
{
|
||||
|
|
308
simpledisplay.d
308
simpledisplay.d
|
@ -1647,6 +1647,60 @@ shared static this() {
|
|||
SystemParametersInfoForDpi = cast(typeof(SystemParametersInfoForDpi)) GetProcAddress(lib, "SystemParametersInfoForDpi");
|
||||
}
|
||||
|
||||
/++
|
||||
Blocking mode for event loop calls associated with a window instance.
|
||||
|
||||
History:
|
||||
Added December 8, 2021 (dub v10.5). Prior to that, all calls to
|
||||
`window.eventLoop` were the same as calls to `EventLoop.get.run`; that
|
||||
is, all would block until the application quit.
|
||||
|
||||
That behavior can still be achieved here with `untilApplicationQuits`,
|
||||
or explicitly calling the top-level `EventLoop.get.run` function.
|
||||
+/
|
||||
enum BlockingMode {
|
||||
/++
|
||||
The event loop call will block until the whole application is ready
|
||||
to quit if it is the only one running, but if it is nested inside
|
||||
another one, it will only block until the window you're calling it on
|
||||
closes.
|
||||
+/
|
||||
automatic = 0x00,
|
||||
/++
|
||||
The event loop call will only return when the whole application
|
||||
is ready to quit. This usually means all windows have been closed.
|
||||
|
||||
This is appropriate for your main application event loop.
|
||||
+/
|
||||
untilApplicationQuits = 0x01,
|
||||
/++
|
||||
The event loop will return when the window you're calling it on
|
||||
closes. If there are other windows still open, they may be destroyed
|
||||
unless you have another event loop running later.
|
||||
|
||||
This might be appropriate for a modal dialog box loop. Remember that
|
||||
other windows are still processing input though, so you can end up
|
||||
with a lengthy call stack if this happens in a loop, similar to a
|
||||
recursive function (well, it literally is a recursive function, just
|
||||
not an obvious looking one).
|
||||
+/
|
||||
untilWindowCloses = 0x02,
|
||||
/++
|
||||
If an event loop is already running, this call will immediately
|
||||
return, allowing the existing loop to handle it. If not, this call
|
||||
will block until the condition you bitwise-or into the flag.
|
||||
|
||||
The default is to block until the application quits, same as with
|
||||
the `automatic` setting (since if it were nested, which triggers until
|
||||
window closes in automatic, this flag would instead not block at all),
|
||||
but if you used `BlockingMode.onlyIfNotNested | BlockingMode.untilWindowCloses`,
|
||||
it will only nest until the window closes. You might want that if you are
|
||||
going to open two windows simultaneously and want closing just one of them
|
||||
to trigger the event loop return.
|
||||
+/
|
||||
onlyIfNotNested = 0x10,
|
||||
}
|
||||
|
||||
/++
|
||||
The flagship window class.
|
||||
|
||||
|
@ -2192,6 +2246,8 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
/// Returns true if the window has been closed.
|
||||
final @property bool closed() { return _closed; }
|
||||
|
||||
private final @property bool notClosed() { return !_closed; }
|
||||
|
||||
/// Returns true if the window is focused.
|
||||
final @property bool focused() { return _focused; }
|
||||
|
||||
|
@ -2405,7 +2461,12 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
|
||||
/++
|
||||
Sets your event handlers, without entering the event loop. Useful if you
|
||||
have multiple windows - set the handlers on each window, then only do eventLoop on your main window.
|
||||
have multiple windows - set the handlers on each window, then only do
|
||||
[eventLoop] on your main window or call `EventLoop.get.run();`.
|
||||
|
||||
This assigns the given handlers to [handleKeyEvent], [handleCharEvent],
|
||||
[handlePulse], and [handleMouseEvent] automatically based on the provide
|
||||
delegate signatures.
|
||||
+/
|
||||
void setEventHandlers(T...)(T eventHandlers) {
|
||||
// FIXME: add more events
|
||||
|
@ -2422,13 +2483,67 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
}
|
||||
}
|
||||
|
||||
/// The event loop automatically returns when the window is closed
|
||||
/// pulseTimeout is given in milliseconds. If pulseTimeout == 0, no
|
||||
/// pulse timer is created. The event loop will block until an event
|
||||
/// arrives or the pulse timer goes off.
|
||||
/++
|
||||
The event loop automatically returns when the window is closed
|
||||
pulseTimeout is given in milliseconds. If pulseTimeout == 0, no
|
||||
pulse timer is created. The event loop will block until an event
|
||||
arrives or the pulse timer goes off.
|
||||
|
||||
The given `eventHandlers` are passed to [setEventHandlers], which in turn
|
||||
assigns them to [handleKeyEvent], [handleCharEvent], [handlePulse], and
|
||||
[handleMouseEvent], based on the signature of delegates you provide.
|
||||
|
||||
Give one with no parameters to set a timer pulse handler. Give one that
|
||||
takes [KeyEvent] for a key handler, [MouseEvent], for a mouse handler,
|
||||
and one that takes `dchar` for a char event handler. You can use as many
|
||||
or as few handlers as you need for your application.
|
||||
|
||||
History:
|
||||
The overload without `pulseTimeout` was added on December 8, 2021.
|
||||
|
||||
On December 9, 2021, the default blocking mode (which is now configurable
|
||||
because [eventLoopWithBlockingMode] was added) switched from
|
||||
[BlockingMode.untilApplicationQuits] over to [BlockingMode.automatic]. This
|
||||
should almost never be noticeable to you since the typical simpledisplay
|
||||
paradigm has been (and I still recommend) to have one `eventLoop` call.
|
||||
|
||||
See_Also:
|
||||
[eventLoopWithBlockingMode]
|
||||
+/
|
||||
final int eventLoop(T...)(
|
||||
long pulseTimeout, /// set to zero if you don't want a pulse.
|
||||
T eventHandlers) /// delegate list like std.concurrency.receive
|
||||
{
|
||||
return eventLoopWithBlockingMode(BlockingMode.automatic, pulseTimeout, eventHandlers);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
final int eventLoop(T...)(T eventHandlers) if(T.length == 0 || is(T[0] == delegate))
|
||||
{
|
||||
return eventLoopWithBlockingMode(BlockingMode.automatic, 0, eventHandlers);
|
||||
}
|
||||
|
||||
/++
|
||||
This is the function [eventLoop] forwards to. It, in turn, forwards to `EventLoop.get.run`.
|
||||
|
||||
History:
|
||||
Added December 8, 2021 (dub v10.5)
|
||||
|
||||
Previously, this implementation was right inside [eventLoop], but when I wanted
|
||||
to add the new [BlockingMode] parameter, the compiler got in a trouble loop so I
|
||||
just renamed it instead of adding as an overload. Besides, the new name makes it
|
||||
easier to remember the order and avoids ambiguity between two int-like params anyway.
|
||||
|
||||
See_Also:
|
||||
[SimpleWindow.eventLoop], [EventLoop]
|
||||
|
||||
Bugs:
|
||||
The blocking mode is not implemented on OSX Cocoa nor on the (deprecated) arsd.eventloop.
|
||||
+/
|
||||
final int eventLoopWithBlockingMode(T...)(
|
||||
BlockingMode blockingMode, /// when you want this function to block until
|
||||
long pulseTimeout, /// set to zero if you don't want a pulse.
|
||||
T eventHandlers) /// delegate list like std.concurrency.receive
|
||||
{
|
||||
setEventHandlers(eventHandlers);
|
||||
|
||||
|
@ -2456,7 +2571,15 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
return 0;
|
||||
} else {
|
||||
EventLoop el = EventLoop(pulseTimeout, handlePulse);
|
||||
return el.run();
|
||||
|
||||
if((blockingMode & BlockingMode.onlyIfNotNested) && el.impl.refcount > 1)
|
||||
return 0;
|
||||
|
||||
return el.run(
|
||||
((blockingMode & 0x0f) == BlockingMode.untilApplicationQuits) ?
|
||||
null :
|
||||
&this.notClosed
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3507,6 +3630,10 @@ struct EventLoop {
|
|||
return EventLoop(0, null);
|
||||
}
|
||||
|
||||
static void quitApplication() {
|
||||
EventLoop.get().exit();
|
||||
}
|
||||
|
||||
__gshared static Object monitor = new Object(); // deliberate CTFE usage here fyi
|
||||
|
||||
/// Construct an application-global event loop for yourself
|
||||
|
@ -4058,8 +4185,10 @@ struct EventLoopImpl {
|
|||
if(count > 10)
|
||||
break; // take the opportunity to catch up on other events
|
||||
|
||||
if(ret == 0) // WM_QUIT
|
||||
if(ret == 0) { // WM_QUIT
|
||||
EventLoop.quitApplication();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if(waitResult == 0x000000C0L /* WAIT_IO_COMPLETION */) {
|
||||
SleepEx(0, true); // I call this to give it a chance to do stuff like async io
|
||||
|
@ -7627,18 +7756,28 @@ final class Image {
|
|||
mixin NativeImageImplementation!() impl;
|
||||
}
|
||||
|
||||
/// A convenience function to pop up a window displaying the image.
|
||||
/// If you pass a win, it will draw the image in it. Otherwise, it will
|
||||
/// create a window with the size of the image and run its event loop, closing
|
||||
/// when a key is pressed.
|
||||
void displayImage(Image image, SimpleWindow win = null) {
|
||||
/++
|
||||
A convenience function to pop up a window displaying the image.
|
||||
If you pass a win, it will draw the image in it. Otherwise, it will
|
||||
create a window with the size of the image and run its event loop, closing
|
||||
when a key is pressed.
|
||||
|
||||
History:
|
||||
`BlockingMode` parameter added on December 8, 2021. Previously, it would
|
||||
always block until the application quit which could cause bizarre behavior
|
||||
inside a more complex application. Now, the default is to block until
|
||||
this window closes if it is the only event loop running, and otherwise,
|
||||
not to block at all and just pop up the display window asynchronously.
|
||||
+/
|
||||
void displayImage(Image image, SimpleWindow win = null, BlockingMode bm = BlockingMode.untilWindowCloses | BlockingMode.onlyIfNotNested) {
|
||||
if(win is null) {
|
||||
win = new SimpleWindow(image);
|
||||
{
|
||||
auto p = win.draw;
|
||||
p.drawImage(Point(0, 0), image);
|
||||
}
|
||||
win.eventLoop(0,
|
||||
win.eventLoopWithBlockingMode(
|
||||
bm, 0,
|
||||
(KeyEvent ev) {
|
||||
if (ev.pressed && (ev.key == Key.Escape || ev.key == Key.Space)) win.close();
|
||||
} );
|
||||
|
@ -14739,8 +14878,10 @@ version(X11) {
|
|||
anyImportant = true;
|
||||
break;
|
||||
}
|
||||
if(!anyImportant)
|
||||
if(!anyImportant) {
|
||||
EventLoop.quitApplication();
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
auto window = e.xdestroywindow.window;
|
||||
if(window in CapableOfHandlingNativeEvent.nativeHandleMapping)
|
||||
|
@ -17609,10 +17750,41 @@ extern(System) nothrow @nogc {
|
|||
void glDeleteShader(GLuint);
|
||||
GLint glGetUniformLocation(GLuint, const(GLchar)*);
|
||||
void glGenBuffers(GLsizei, GLuint*);
|
||||
void glUniform4fv(GLint, GLsizei, const(GLfloat)*);
|
||||
void glUniform1f(GLint, float);
|
||||
void glUniform2f(GLint, float, float);
|
||||
void glUniform4f(GLint, float, float, float, float);
|
||||
|
||||
void glUniform1f(GLint location, GLfloat v0);
|
||||
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
|
||||
void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
|
||||
void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
|
||||
void glUniform1i(GLint location, GLint v0);
|
||||
void glUniform2i(GLint location, GLint v0, GLint v1);
|
||||
void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2);
|
||||
void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
|
||||
void glUniform1ui(GLint location, GLuint v0);
|
||||
void glUniform2ui(GLint location, GLuint v0, GLuint v1);
|
||||
void glUniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2);
|
||||
void glUniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
|
||||
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value);
|
||||
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value);
|
||||
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value);
|
||||
void glUniform4fv(GLint location, GLsizei count, const GLfloat *value);
|
||||
void glUniform1iv(GLint location, GLsizei count, const GLint *value);
|
||||
void glUniform2iv(GLint location, GLsizei count, const GLint *value);
|
||||
void glUniform3iv(GLint location, GLsizei count, const GLint *value);
|
||||
void glUniform4iv(GLint location, GLsizei count, const GLint *value);
|
||||
void glUniform1uiv(GLint location, GLsizei count, const GLuint *value);
|
||||
void glUniform2uiv(GLint location, GLsizei count, const GLuint *value);
|
||||
void glUniform3uiv(GLint location, GLsizei count, const GLuint *value);
|
||||
void glUniform4uiv(GLint location, GLsizei count, const GLuint *value);
|
||||
void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
void glUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
|
||||
|
||||
void glColorMask(GLboolean, GLboolean, GLboolean, GLboolean);
|
||||
void glStencilOpSeparate(GLenum, GLenum, GLenum, GLenum);
|
||||
void glDrawArrays(GLenum, GLint, GLsizei);
|
||||
|
@ -18051,6 +18223,10 @@ final class OpenGlShader {
|
|||
if(id != -1)
|
||||
glUniform2f(id, x, y);
|
||||
}
|
||||
|
||||
void opAssign(T)(T t) {
|
||||
t.glUniform(id);
|
||||
}
|
||||
}
|
||||
|
||||
static struct UniformsHelper {
|
||||
|
@ -18063,6 +18239,11 @@ final class OpenGlShader {
|
|||
//throw new Exception("Could not find uniform " ~ name);
|
||||
return Uniform(i);
|
||||
}
|
||||
|
||||
@property void opDispatch(string name, T)(T t) {
|
||||
Uniform f = this.opDispatch!name;
|
||||
t.glUniform(f);
|
||||
}
|
||||
}
|
||||
|
||||
/++
|
||||
|
@ -18072,6 +18253,97 @@ final class OpenGlShader {
|
|||
@property UniformsHelper uniforms() { return UniformsHelper(this); }
|
||||
}
|
||||
|
||||
version(without_opengl) {} else {
|
||||
/++
|
||||
A static container of types and value constructors for opengl 3+ shaders.
|
||||
|
||||
|
||||
You can declare variables like:
|
||||
|
||||
```
|
||||
OGL.vec3f something;
|
||||
```
|
||||
|
||||
But generally it would be used with [OpenGlShader]'s uniform helpers like
|
||||
|
||||
```
|
||||
shader.uniforms.mouse = OGL.vec(mouseX, mouseY); // or OGL.vec2f if you want to be more specific
|
||||
```
|
||||
+/
|
||||
final class OGL {
|
||||
static:
|
||||
|
||||
private template typeFromSpecifier(string specifier) {
|
||||
static if(specifier == "f")
|
||||
alias typeFromSpecifier = GLfloat;
|
||||
else static if(specifier == "i")
|
||||
alias typeFromSpecifier = GLint;
|
||||
else static if(specifier == "ui")
|
||||
alias typeFromSpecifier = GLuint;
|
||||
else static assert(0, "I don't know this ogl type suffix " ~ specifier);
|
||||
}
|
||||
|
||||
private template CommonType(T...) {
|
||||
static if(T.length == 1)
|
||||
alias CommonType = T[0];
|
||||
else static if(is(typeof(true ? T[0].init : T[1].init) C))
|
||||
alias CommonType = CommonType!(C, T[2 .. $]);
|
||||
}
|
||||
|
||||
private template typesToSpecifier(T...) {
|
||||
static if(is(CommonType!T == float))
|
||||
enum typesToSpecifier = "f";
|
||||
else static if(is(CommonType!T == int))
|
||||
enum typesToSpecifier = "i";
|
||||
else static if(is(CommonType!T == uint))
|
||||
enum typesToSpecifier = "ui";
|
||||
else static assert(0, "I can't find a gl type suffix for common type " ~ CommonType!T.stringof);
|
||||
}
|
||||
|
||||
private template genNames(size_t dim) {
|
||||
string helper() {
|
||||
string s;
|
||||
if(dim > 0) s ~= "type x = 0;";
|
||||
if(dim > 1) s ~= "type y = 0;";
|
||||
if(dim > 2) s ~= "type z = 0;";
|
||||
if(dim > 3) s ~= "type w = 0;";
|
||||
return s;
|
||||
}
|
||||
|
||||
enum genNames = helper();
|
||||
}
|
||||
|
||||
// there's vec, arrays of vec, mat, and arrays of mat
|
||||
template opDispatch(string name)
|
||||
if(name.length > 4 && (name[0 .. 3] == "vec" || name[0 .. 3] == "mat"))
|
||||
{
|
||||
// FIXME: matrix can be this or nx4 etc
|
||||
enum dim = cast(int) (name[3] - '0');
|
||||
static assert(dim > 0 && dim <= 4, "Bad dimension for OGL type " ~ name[3]);
|
||||
enum isArray = name[$ - 1] == 'v';
|
||||
enum typeSpecifier = isArray ? name[4 .. $ - 1] : name[4 .. $];
|
||||
alias type = typeFromSpecifier!typeSpecifier;
|
||||
|
||||
align(1)
|
||||
struct opDispatch {
|
||||
align(1):
|
||||
mixin(genNames!dim);
|
||||
|
||||
private void glUniform(OpenGlShader.Uniform assignTo) {
|
||||
glUniform(assignTo.id);
|
||||
}
|
||||
private void glUniform(int assignTo) {
|
||||
mixin("glUniform" ~ name[3 .. $])(assignTo, this.tupleof);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto vec(T...)(T members) {
|
||||
return typeof(this).opDispatch!("vec" ~ toInternal!string(cast(int) T.length)~ typesToSpecifier!T)(members);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
version(linux) {
|
||||
version(with_eventloop) {} else {
|
||||
private int epollFd = -1;
|
||||
|
|
Loading…
Reference in New Issue