arsd.simpledisplay
simpledisplay.d provides basic cross-platform GUI-related functionality,
including creating windows, drawing on them, working with the clipboard,
timers, OpenGL, and more. However, it does NOT provide high level GUI
widgets. See my minigui.d, an extension to this module, for that
functionality.
Discussion
simpledisplay provides cross-platform wrapping for Windows and Linux
(and perhaps other OSes that use X11), but also does not prevent you
from using the underlying facilities if you need them. It has a goal
of working efficiently over a remote X link (at least as far as Xlib
reasonably allows.)
simpledisplay depends on [arsd.color|color.d], which should be available from the
same place where you got this file. Other than that, however, it has
very few dependencies and ones that don't come with the OS and/or the
compiler are all opt-in.
simpledisplay.d's home base is on my arsd repo on Github. The file is:
https:
//github.com/adamdruppe/arsd/blob/master/simpledisplay.d
simpledisplay is basically stable. I plan to refactor the internals,
and may add new features and fix bugs, but It do not expect to
significantly change the API. It has been stable a few years already now.
Jump list:
Don't worry, you don't have to read this whole documentation file!
Check out the [#Event-example] and [#Pong-example] to get started quickly.
The main classes you may want to create are [SimpleWindow], [Timer],
[Image], and [Sprite].
The main functions you'll want are [setClipboardText] and [getClipboardText].
There are also platform-specific functions available such as [XDisplayConnection]
and [GetAtom] for X11, among others.
See the examples and topics list below to learn more.
The goal here is to give some complete programs as overview examples first, then a look at each major feature with working examples first, then, finally, the inline class and method list will follow.
Scan for headers for a topic - they will visually stand out - you're interested in to get started quickly and feel free to copy and paste any example as a starting point for your program. I encourage you to learn the library by experimenting with the examples!
All examples are provided with no copyright restrictions whatsoever. You do not need to credit me or carry any kind of notice with the source if you copy and paste from them.
To get started, download and simpledisplay.dcolor.d to a working directory. Copy an example info a file called example.d and compile using the command given at the top of each example.
If you need help, email me: destructionator@gmail.com or IRC us, #d on Freenode (I am destructionator or adam_d_ruppe there). If you learn something that isn't documented, I appreciate pull requests on github to this file.
At points, I will talk about implementation details in the documentation. These are sometimes
subject to change, but nevertheless useful to understand what is really going on. You can learn
more about some of the referenced things by searching the web for info about using them from C.
You can always look at the source of simpledisplay.d too for the most authoritative source on
its specific implementation. If you disagree with how I did something, please contact me so we
can discuss it!
Examples
This program creates a window and draws events inside them as they
happen, scrolling the text in the window as needed. Run this program
and experiment to get a feel for where basic input events take place
in the library.
// dmd example.d simpledisplay.d color.d
import arsd.simpledisplay;
import std.conv;
void main() {
auto window = new SimpleWindow(Size(500, 500), "My D App");
int y = 0;
void addLine(string text) {
auto painter = window.draw();
if(y + painter.fontHeight >= window.height) {
painter.scrollArea(Point(0, 0), window.width, window.height, 0, painter.fontHeight);
y -= painter.fontHeight;
}
painter.outlineColor = Color.red;
painter.fillColor = Color.black;
painter.drawRectangle(Point(0, y), window.width, painter.fontHeight);
painter.outlineColor = Color.white;
painter.drawText(Point(10, y), text);
y += painter.fontHeight;
}
window.eventLoop(1000,
() {
addLine("Timer went off!");
},
(KeyEvent event) {
addLine(to!string(event));
},
(MouseEvent event) {
addLine(to!string(event));
},
(dchar ch) {
addLine(to!string(ch));
}
);
}
// dmd example.d simpledisplay.d color.d
import arsd.simpledisplay;
enum paddleMovementSpeed = 8;
enum paddleHeight = 48;
void main() {
auto window = new SimpleWindow(600, 400, "Pong game!");
int playerOnePosition, playerTwoPosition;
int playerOneMovement, playerTwoMovement;
int playerOneScore, playerTwoScore;
int ballX, ballY;
int ballDx, ballDy;
void serve() {
import std.random;
ballX = window.width / 2;
ballY = window.height / 2;
ballDx = uniform(-4, 4) * 3;
ballDy = uniform(-4, 4) * 3;
if(ballDx == 0)
ballDx = uniform(0, 2) == 0 ? 3 : -3;
}
serve();
window.eventLoop(50, // set a 50 ms timer pulls
// This runs once per timer pulse
delegate () {
auto painter = window.draw();
painter.clear();
// Update everyone's motion
playerOnePosition += playerOneMovement;
playerTwoPosition += playerTwoMovement;
ballX += ballDx;
ballY += ballDy;
// Bounce off the top and bottom edges of the window
if(ballY + 7 >= window.height)
ballDy = -ballDy;
if(ballY - 8 <= 0)
ballDy = -ballDy;
// Bounce off the paddle, if it is in position
if(ballX - 8 <= 16) {
if(ballY + 7 > playerOnePosition && ballY - 8 < playerOnePosition + paddleHeight) {
ballDx = -ballDx + 1; // add some speed to keep it interesting
ballDy += playerOneMovement; // and y movement based on your controls too
ballX = 24; // move it past the paddle so it doesn't wiggle inside
} else {
// Missed it
playerTwoScore ++;
serve();
}
}
if(ballX + 7 >= window.width - 16) { // do the same thing but for player 1
if(ballY + 7 > playerTwoPosition && ballY - 8 < playerTwoPosition + paddleHeight) {
ballDx = -ballDx - 1;
ballDy += playerTwoMovement;
ballX = window.width - 24;
} else {
// Missed it
playerOneScore ++;
serve();
}
}
// Draw the paddles
painter.outlineColor = Color.black;
painter.drawLine(Point(16, playerOnePosition), Point(16, playerOnePosition + paddleHeight));
painter.drawLine(Point(window.width - 16, playerTwoPosition), Point(window.width - 16, playerTwoPosition + paddleHeight));
// Draw the ball
painter.fillColor = Color.red;
painter.outlineColor = Color.yellow;
painter.drawEllipse(Point(ballX - 8, ballY - 8), Point(ballX + 7, ballY + 7));
// Draw the score
painter.outlineColor = Color.blue;
import std.conv;
painter.drawText(Point(64, 4), to!string(playerOneScore));
painter.drawText(Point(window.width - 64, 4), to!string(playerTwoScore));
},
delegate (KeyEvent event) {
// Player 1's controls are the arrow keys on the keyboard
if(event.key == Key.Down)
playerOneMovement = event.pressed ? paddleMovementSpeed : 0;
if(event.key == Key.Up)
playerOneMovement = event.pressed ? -paddleMovementSpeed : 0;
},
delegate (MouseEvent event) {
// Player 2's controls are mouse movement while the left button is held down
if(event.type == MouseEventType.motion && (event.modifierState & ModifierState.leftButtonDown)) {
if(event.dy > 0)
playerTwoMovement = paddleMovementSpeed;
else if(event.dy < 0)
playerTwoMovement = -paddleMovementSpeed;
} else {
playerTwoMovement = 0;
}
}
);
}
simpledisplay, and its other stand-alone support modules, simpleaudio.d and joystick.d, too.
This program displays a pie chart. Clicking on a color will increase its share of the pie.
simpledisplay's flagship feature. It represents a single
window on the user's screen.
You may create multiple windows, if the underlying platform supports it. You may check
static if(multipleWindowsSupported) at compile time, or catch exceptions thrown by
SimpleWindow's constructor at runtime to handle those cases.
A single running event loop will handle as many windows as needed.
setEventHandlers function
eventLoop function
draw function
title property
The simpledisplay event loop is designed to handle common cases easily while being extensible for more advanced cases, or replaceable by other libraries.
The most common scenario is creating a window, then calling window.eventLoop when setup is complete. You can pass several handlers to the eventLoop method right there:
// dmd example.d simpledisplay.d color.d
import arsd.simpledisplay;
void main() {
auto window = new SimpleWindow(200, 200);
window.eventLoop(0,
delegate (dchar) { /* got a character key press */ }
);
}
epoll system call for efficiency an extensibility to other files. On Windows, it runs a traditional GetMessage + DispatchMessage loop, with a call to SleepEx in each iteration to allow the thread to enter an alertable wait state regularly, primarily so Overlapped I/O callbacks will get a chance to run.
On Linux, simpledisplay also supports my arsd.eventloop module. Compile your program, including the eventloop.d file, with the -version=with_eventloop switch.
It should be possible to integrate simpledisplay with vibe.d as well, though I haven't tried.
Notification area icons are currently only implemented on X11 targets. Windows support will come when I need it (or if someone requests it and I have some time to spend on it).
There are event handlers for low-level keyboard and mouse events, and higher level handlers for character events.
To draw on your window, use the window.draw method. It returns a [ScreenPainter] structure with drawing methods.
Important:
ScreenPainter double-buffers and will not actually update the window until its destructor is run. Always ensure the painter instance goes out-of-scope before proceeding. You can do this by calling it inside an event handler, a timer callback, or an small scope inside main. For example:
// dmd example.d simpledisplay.d color.d
import arsd.simpledisplay;
void main() {
auto window = new SimpleWindow(200, 200);
{ // introduce sub-scope
auto painter = window.draw(); // begin drawing
/* draw here */
painter.outlineColor = Color.red;
painter.fillColor = Color.black;
painter.drawRectangle(Point(0, 0), 200, 200);
} // end scope, calling `painter`'s destructor, drawing to the screen.
window.eventLoop(0); // handle events
}
simpledisplay can create OpenGL contexts on your window. It works quite differently than 2d drawing.
Note that it is still possible to draw 2d on top of an OpenGL window, using the draw method, though I don't recommend it.
To start, you create a SimpleWindow with OpenGL enabled by passing the argument OpenGlOptions.yes to the constructor.
Next, you set redrawOpenGlScene to a delegate which draws your frame.
To force a redraw of the scene, call window.redrawOpenGlSceneNow().
Please note that my experience with OpenGL is very out-of-date, and the bindings in simpledisplay reflect that. If you want to use more modern functions, you may have to define the bindings yourself, or import them from another module. However, I believe the OpenGL context creation done in simpledisplay will work for any version.
This example program will draw a rectangle on your window:
// dmd example.d simpledisplay.d color.d
import arsd.simpledisplay;
void main() {
}
png.d.
// dmd example.d simpledisplay.d color.d png.d
import arsd.simpledisplay;
import arsd.png;
void main() {
auto image = Image.fromMemoryImage(readPng("image.png"));
displayImage(image);
}
dmd example.d .
If you find an image file which is a valid png that simpledisplay.d png.darsd.png fails to load, please let me know. In the mean time of fixing the bug, you can probably convert the file into an easier-to-load format. Be sure to turn OFF png interlacing, as that isn't supported. Other things to try would be making the image smaller, or trying 24 bit truecolor mode with an alpha channel.
The [Sprite] class is used to make images on the display server for fast blitting to screen. This is especially important to use to support fast drawing of repeated images on a remote X11 link.
The free functions [getClipboardText] and [setClipboardText] consist of simpledisplay's cross-platform clipboard support at this time.
It also has helpers for handling X-specific events.
There are two timers in simpledisplay: one is the pulse timeout you can set on the call to window.eventLoop, and the other is a customizable class, [Timer].
The pulse timeout is used by setting a non-zero interval as the first argument to eventLoop function and adding a zero-argument delegate to handle the pulse.
import arsd.simpledisplay;
void main() {
auto window = new SimpleWindow(400, 400);
// every 100 ms, it will draw a random line
// on the window.
window.eventLoop(100, {
auto painter = window.draw();
import std.random;
// random color
painter.outlineColor = Color(uniform(0, 256), uniform(0, 256), uniform(0, 256));
// random line
painter.drawLine(
Point(uniform(0, window.width), uniform(0, window.height)),
Point(uniform(0, window.width), uniform(0, window.height)));
});
}
Timer class works similarly, but is created separately from the event loop. (It still fires through the event loop, though.) You may make as many instances of Timer as you wish.
The pulse timer and instances of the [Timer] class may be combined at will.
import arsd.simpledisplay;
void main() {
auto window = new SimpleWindow(400, 400);
auto timer = new Timer(1000, delegate {
auto painter = window.draw();
painter.clear();
});
window.eventLoop(0);
}
SetTimer and Linux, using timerfd_create. These deliver timeout messages through your application event loop.
simpledisplay carries a lot of code to help implement itself without extra dependencies, and much of this code is available for you too, so you may extend the functionality yourself.
See also: xwindows.d from my github.
handleNativeEvent and handleNativeGlobalEvent.
Integration with a third-party event loop is possible.
On Linux, you might want to support both terminal input and GUI input. You can do this by using simpledisplay together with eventloop.d and terminal.d.
simpledisplay does not provide GUI widgets such as text areas, buttons, checkboxes, etc. It only gives basic windows, the ability to draw on it, receive input from it, and access native information for extension. You may write your own gui widgets with these, but you don't have to because I already did for you!
Download minigui.d from my github repository and add it to your project. minigui builds these things on top of simpledisplay and offers its own Window class (and subclasses) to use that wrap SimpleWindow, adding a new event and drawing model that is hookable by subwidgets, represented by their own classes.
Migrating to minigui from simpledisplay is often easy though, because they both use the same ScreenPainter API, and the same simpledisplay events are available, if you want them. (Though you may like using the minigui model, especially if you are familiar with writing web apps in the browser with Javascript.)
minigui still needs a lot of work to be finished at this time, but it already offers a number of useful classes.
I don't have a Mac, so that code isn't maintained. I would like to have a Cocoa
implementation though.
The NativeSimpleWindowImplementation and NativeScreenPainterImplementation both
suck. If I was rewriting it, I wouldn't do it that way again.
This file must not have any more required dependencies. If you need bindings, add
them right to this file. Once it gets into druntime and is there for a while, remove
bindings from here to avoid conflicts (or put them in an appropriate version block
so it continues to just work on old dmd), but wait a couple releases before making the
transition so this module remains usable with older versions of dmd.
You may have optional dependencies if needed by putting them in version blocks or
template functions. You may also extend the module with other modules with UFCS without
actually editing this - that is nice to do if you can.
Try to make functions work the same way across operating systems. I typically make
it thinly wrap Windows, then emulate that on Linux.
A goal of this is to keep a gui hello world to less than 250 KB. This means avoiding
Phobos! So try to avoid it.
See more comments throughout the source.
I realize this file is fairly large, but over half that is just bindings at the bottom
or documentation at the top. Some of the classes are a bit big too, but hopefully easy
to understand. I suggest you jump around the source by looking for a particular
declaration you're interested in, like class SimpleWindow using your editor's search
function, then look at one piece at a time.
License
Copyright Adam D. Ruppe, 2011-2016. Released under the Boost Software License.
Building documentation: You may wish to use the arsd.ddoc file from my github with
building the documentation for simpledisplay yourself. It will give it a bit more style.
Simply download the arsd.ddoc file and add it to your compile command when building docs.
dmd -c
simpledisplay.d color.d -D arsd.ddoc
-
Declaration
enum boolUsingSimpledisplayX11;If you have to get down and dirty with implementation details, this helps figure out if X is available you can
static if(more reliably thanUsingSimpledisplayX11) ...version()becauseversionis module-local. -
Declaration
enum boolmultipleWindowsSupported;Does this platform support multiple windows? If not, trying to create another will cause it to throw an exception.
-
Declaration
enumWindowFlags: int;After selecting a type from [WindowTypes], you may further customize its behavior by setting one or more of these flags.
Discussion
The different window types have different meanings of
normal. If the window type already is a good match for what you want to do, you should just use [WindowFlags.normal], the default, which will do the right thing for your users. -
Declaration
enumWindowTypes: int;When creating a window, you can pass a type to SimpleWindow's constructor, then further customize the window by changing
WindowFlags.Discussion
This list is based on the EMWH spec for X11.
http: //standards.freedesktop.org/wm-spec/1.4/ar01s05.html#idm139704063786896
-
Declaration
normalAn ordinary application window.
-
Declaration
undecoratedA generic window without a title bar or border. You can draw on the entire area of the screen it takes up and use it as you wish. Remember that users don't really expect these though, so don't use it where a window of any other type is appropriate.
-
-
Declaration
voidsetOpenGLContextVersion()(ubytehi, ubytelo);Set OpenGL context version to use. This has no effect on non-OpenGL windows. You may want to change context version if you want to use advanced shaders or other modern OpenGL techinques. This setting doesn't affect already created windows. You may use version 2.1 as your default, which should be supported by any box since 2006, so seems to be a reasonable choice.
Discussion
Note that by default version is set to
0, which forces SimpleDisplay to use old context creation code without any version specified. This is the safest way to init OpenGL, but it may not give you access to advanced features. See available OpenGL versions here: https://en.wikipedia.org/wiki/OpenGL -
Declaration
@property voidopenGLContextCompatible()(boolv);Set OpenGL context mode. Modern (3.0+) OpenGL versions deprecated old fixed pipeline functions, and without "compatible" mode you won't be able to use your old non-shader-based code with such contexts. By default SimpleDisplay creates compatible context, so you can gradually upgrade your OpenGL code if you want to (or leave it as is, as it should "just work").
-
Declaration
@property voidopenGLContextAllowFallback()(boolv);Set to
trueto allow creating OpenGL context with lower version than requested instead of throwing. If fallback was activated (or legacy OpenGL was requested),openGLContextFallbackActivated()will returntrue. -
Declaration
@property boolopenGLContextFallbackActivated()();After creating OpenGL window, you can check this to see if you got only "legacy" OpenGL context.
-
Declaration
voidsdpyWindowClass(const(char)[]v);Set window class name for all following
new SimpleWindow()calls.Discussion
WARNING! For Windows, you should set your class name before creating any window, and NEVER change it after that!
-
Declaration
stringsdpyWindowClass();Get current window class name.
-
Declaration
classSimpleWindow: arsd.simpledisplay.CapableOfHandlingNativeEvent;The flagship window class.
Discussion
SimpleWindowtries to make ordinary windows very easy to create and use without locking you out of more advanced or complex features of the underlying windowing system. For many applications, you can simply callnewand get a suitable window to work with. From there, you can opt into additional features, like custom resizability and OpenGL support with the next two constructor arguments. Or, if you need even more, you can set a window type and customization flags with the final two constructor arguments. If none of that works for you, you can also create a window using native function calls, then wrap the window in aSimpleWindow(some_width, some_height, "some title")SimpleWindowinstance by callingnew. Remember, though, if you do this, managing the window is still your own responsibility! Notably, you will need to destroy it yourself.SimpleWindow(native_handle)-
Declaration
this(intwidth= 640, intheight= 480, stringtitle= null, OpenGlOptionsopengl= OpenGlOptions.no, Resizablityresizable= Resizablity.automaticallyScaleIfPossible, WindowTypeswindowType= WindowTypes.normal, intcustomizationFlags= WindowFlags.normal, SimpleWindowparent= null);This creates a window with the given options. The window will be visible and able to receive input as soon as you start your event loop. You may draw on it immediately after creating the window, without needing to wait for the event loop to start if you want.
Discussion
The constructor tries to have sane default arguments, so for many cases, you only need to provide a few of them.
Parameters
intwidththe
widthof the window's client area, in pixelsintheightthe
heightof the window's client area, in pixelsstringtitlethe
titleof the window (seen in thetitlebar, taskbar, etc.). You can change it after construction with the [SimpleWindow.title\ property.OpenGlOptionsopengl[OpenGlOptions] are yes and no. If yes, it creates an OpenGL context on the window.
Resizablityresizable[Resizablity] has three options:
allowResizing, which allows the window to be resized by the user. ThewindowResizeddelegate will be called when the size is changed.fixedSizewill not allow the user to resize the window.automaticallyScaleIfPossiblewill allow the user to resize, but will still present the original size to the API user. The contents you draw will be scaled to the size the user chose. If this scaling is not efficient, the window will be fixed size. ThewindowResizedevent handler will never be called. This is the default.WindowTypeswindowTypeThe type of window you want to make.
intcustomizationFlagsA way to make a window without a border, always on top, skip taskbar, and more. Do not use this if one of the pre-defined [WindowTypes], given in the
argument, is a good match for what you need.windowTypeSimpleWindowparentthe
parentwindow, if applicable -
Declaration
this(Sizesize, stringtitle= null, OpenGlOptionsopengl= OpenGlOptions.no, Resizablityresizable= Resizablity.automaticallyScaleIfPossible);Same as above, except using the
Sizestruct instead of separate width and height. -
Declaration
this(Imageimage, stringtitle= null);Creates a window based on the given [Image]. It's client area width and height is equal to the
image. (A window's client area is the drawable space inside; it excludes thetitlebar, etc.)Discussion
Windows based on images will not be resizable and do not use OpenGL.
-
Declaration
this(NativeWindowHandlenativeWindow);Wraps a native window handle with very little additional processing - notably no destruction this is incomplete so don't use it for much right now. The purpose of this is to make native windows created through the low level API (so you can use platform-specific options and other details SimpleWindow does not expose) available to the event loop wrappers.
-
Declaration
voidrequestAttention();Requests attention from the user for this window.
Discussion
The typical result of this function is to change the color of the taskbar icon, though it may be tweaked on specific platforms. It is meant to unobtrusively tell the user that something relevant to them happened in the background and they should check the window when they get a chance. Upon receiving the keyboard focus, the window will automatically return to its natural state. If the window already has the keyboard focus, this function may do nothing, because the user is presumed to already be giving the window attention.
Implementation note:
uses the NET_WM_STATE_DEMANDS_ATTENTION atom on X11 and the FlashWindow function on Windows.requestAttention -
Declaration
void delegate()closeQuery;This will be called when WM wants to close your window (i.e. user clicked "close" icon, for example).
Discussion
You'll have to call
close()manually if you set this delegate. -
Declaration
void delegate(bool becomesVisible)visibilityChanged;This will be called when window visibility was changed.
-
Declaration
final @property boolclosed();Returns
trueif the window has beenclosed. -
Declaration
final @property boolvisible();Returns
trueif the window isvisible(mapped). -
Declaration
voidclose();Closes the window. If there are no more open windows, the event loop will terminate.
-
Declaration
voidshow();Alias for
hidden = false -
Declaration
voidhide();Alias for
hidden = true -
Declaration
voidhideCursor();Hide cursor when it enters the window.
-
Declaration
voidshowCursor();Don't hide cursor when it enters the window.
-
Declaration
boolwarpMouse(intx, inty);"Warp" mouse pointer to coordinates relative to window top-left corner. Return "success" flag.
Discussion
Currently only supported on X11, so Windows implementation will return
false.Note: "warping" pointer will not send any synthesised mouse events, so you probably doesn't want to use it to move mouse pointer to some active GUI area, for example, as your window won't receive "mouse moved here" event.
-
Declaration
voidsendDummyEvent();Send dummy window event to ping event loop. Required to process NotificationIcon on X11, for example.
-
Declaration
voidsetMinSize(intminwidth, intminheight);Set window minimal size.
-
Declaration
voidsetMaxSize(intmaxwidth, intmaxheight);Set window maximal size.
-
Declaration
voidsetResizeGranularity(intgranx, intgrany);Set window resize step (window size will be changed with the given granularity on supported platforms).
Discussion
Currently only supported on X11.
-
Declaration
voidmove(intx, inty);Move window.
-
Declaration
voidresize(intw, inth);Resize window.
-
Declaration
voidmoveResize(intx, inty, intw, inth);Move and resize window (this can be faster and more visually pleasant than doing it separately).
-
Declaration
final @property boolhidden();Returns
trueif the window ishidden. -
Declaration
final @property voidhidden(boolb);Shows or hides the window based on the bool argument.
-
Declaration
voidsetEventHandlers(T...)(TeventHandlers);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.
-
Declaration
final inteventLoop(T...)(longpulseTimeout, TeventHandlers);The event loop automatically returns when the window is closed
Discussion
pulseTimeoutis given in milliseconds. IfpulseTimeout== 0, no pulse timer is created. The event loop will block until an event arrives or the pulse timer goes off. -
Declaration
ScreenPainterdraw();This lets you
drawon the window (or its backing buffer) using basic 2D primitives.Discussion
Be sure to call this in a limited scope because your changes will not actually appear on the window until ScreenPainter's destructor runs.
Return Value
an instance of [ScreenPainter], which has the drawing methods on it to
drawon this window. -
Declaration
final @property intwidth();Width of the window's drawable client area, in pixels.
-
Declaration
final @property intheight();Height of the window's drawable client area, in pixels.
-
Declaration
void delegate()redrawOpenGlScene;Put your code in here that you want to be drawn automatically when your window is uncovered. Set a handler here *before* entering your event loop any time you pass
OpenGlOptions.yesto the constructor. Ideally, you will set this delegate immediately after constructing theSimpleWindow. -
Declaration
final @property voidvsync(boolwait);This will allow you to change OpenGL
vsyncstate. -
Declaration
voidmtUnlock();"Unlock" this window handle, to do multithreaded synchronization. You probably won't need
Discussion
to call this, as it's not recommended to share window between threads.
-
Declaration
voidbeep();Emit a
beepto get user's attention. -
Declaration
booluseGLFinish;Set this to
falseif you don't need to doglFinish()afterswapOpenGlBuffers().Discussion
Note that at least NVidia proprietary driver may segfault if you will modify texture fast enough without waiting 'em to finish their frame bussiness.
-
Declaration
voidredrawOpenGlSceneNow();call this to invoke your delegate. It automatically sets up the context and flips the buffer. If you need to redraw the scene in response to an event, call this.
-
Declaration
voidsetAsCurrentOpenGlContext();Makes all gl* functions target this window until changed. This is only valid if you passed
OpenGlOptions.yesto the constructor. -
Declaration
nothrow boolsetAsCurrentOpenGlContextNT();Makes all gl* functions target this window until changed. This is only valid if you passed
OpenGlOptions.yesto the constructor.Discussion
This doesn't throw, returning success flag instead.
-
Declaration
nothrow boolreleaseCurrentOpenGlContext();Releases OpenGL context, so it can be reused in, for example, different thread. This is only valid if you passed
OpenGlOptions.yesto the constructor.Discussion
This doesn't throw, returning success flag instead.
-
Declaration
voidswapOpenGlBuffers();simpledisplay always uses double buffering, usually automatically. This manually swaps the OpenGL buffers.
Discussion
You should not need to call this yourself because simpledisplay will do it for you after calling your
redrawOpenGlScene. Remember that this may throw an exception, which you can catch in a multithreaded application to keep your thread from dying from an unhandled exception. -
Declaration
@property voidtitle(stringtitle);Set the window
title, which is visible on the window managertitlebar, operating system taskbar, etc.Discussion
You may call this function at any time.auto window = new SimpleWindow(100, 100, "First title"); window.title = "A new title";
-
Declaration
@property stringtitle();Gets the
title -
Declaration
@property voidicon(MemoryImageicon);Set the
iconthat is seen in the title bar or taskbar, etc., for the user. -
Declaration
@property voidimage(Imagei);Draws an
imageon the window. This is meant to provide quick lookDiscussion
of a static
imagegenerated elsewhere. -
Declaration
void delegate(KeyEvent ke)handleKeyEvent;What follows are the event handlers. These are set automatically
Discussion
by the eventLoop function, but are still public so you can change them later. wasPressed ==
truemeans key down.false== key up. Handles a low-level keyboard event. Settable through setEventHandlers. -
Declaration
void delegate(dchar c)handleCharEvent;Handles a higher level keyboard event - c is the character just pressed. Settable through setEventHandlers.
-
Declaration
void delegate()handlePulse;Handles a timer pulse. Settable through setEventHandlers.
-
Declaration
void delegate(bool)onFocusChange;called when the focus changes, param is if we have it (
true) or are losing it (false) -
Declaration
void delegate(MouseEvent)handleMouseEvent;Mouse event handler. Settable through setEventHandlers.
-
Declaration
void delegate()paintingFinished;use to redraw child widgets if you use system apis to add stuff
-
Declaration
void delegate(int width, int height)windowResized;handle a resize, after it happens. You must construct the window with Resizablity.allowResizing
Discussion
for this to ever happen.
-
Declaration
NativeEventHandlerhandleNativeEvent;Platform specific - handle any native messages this window gets.
Note: this is called *in addition to* other event handlers, unless you return zero indicating that you handled it. On Windows, it takes the form of int delegate(HWND,UINT, WPARAM, LPARAM). On X11, it takes the form of int delegate(XEvent).
IMPORTANT: it used to be static in old versions of simpledisplay.d, but I always used it as if it wasn't static... so now I just fixed it so it isn't anymore.
-
Declaration
static NativeEventHandlerhandleNativeGlobalEvent;This is the same as handleNativeEvent, but static so it can hook ALL events in the loop.
Discussion
If you used to use handleNativeEvent depending on it being static, just change it to use this instead and it will work the same way.
-
Declaration
@property booleventQueueEmpty()();Is our custom event queue empty? Can be used in simple cases to prevent "spamming" window with events it can't cope with. It is safe to call this from non-UI threads.
Discussion
This is in-process one-way (from anything to window) event sending mechanics. It is thread-safe, so it can be used in multi-threaded applications to send, for example, "wake up and repaint" events when thread completed some operation. This will allow to avoid using timer pulse to check events with synchronization, 'cause event handler will be called in UI thread. You can stop guessing which pulse frequency will be enough for your app. Note that events handlers may be called in arbitrary order, i.e. last registered handler can be called first, and vice versa.
-
Declaration
@property booleventQueued(ET : Object)();Does our custom event queue contains at least one with the given type? Can be used in simple cases to prevent "spamming" window with events it can't cope with. It is safe to call this from non-UI threads.
Discussion
This is in-process one-way (from anything to window) event sending mechanics. It is thread-safe, so it can be used in multi-threaded applications to send, for example, "wake up and repaint" events when thread completed some operation. This will allow to avoid using timer pulse to check events with synchronization, 'cause event handler will be called in UI thread. You can stop guessing which pulse frequency will be enough for your app. Note that events handlers may be called in arbitrary order, i.e. last registered handler can be called first, and vice versa.
-
Declaration
uintaddEventListener(ET : Object)(void delegate(ET)dg);Add listener for custom event. Can be used like this:
Discussion
auto eid = win.addEventListener((MyStruct evt) { ... }); ... win.removeEventListener(eid);
Return Value
0 on failure (should never happen, so ignore it) This is in-process one-way (from anything to window) event sending mechanics. It is thread-safe, so it can be used in multi-threaded applications to send, for example, "wake up and repaint" events when thread completed some operation. This will allow to avoid using timer pulse to check events with synchronization, 'cause event handler will be called in UI thread. You can stop guessing which pulse frequency will be enough for your app. Note that events handlers may be called in arbitrary order, i.e. last registered handler can be called first, and vice versa.
-
Declaration
voidremoveEventListener()(uintid);Remove event listener. It is safe to pass invalid event
idhere.Discussion
This is in-process one-way (from anything to window) event sending mechanics. It is thread-safe, so it can be used in multi-threaded applications to send, for example, "wake up and repaint" events when thread completed some operation. This will allow to avoid using timer pulse to check events with synchronization, 'cause event handler will be called in UI thread. You can stop guessing which pulse frequency will be enough for your app. Note that events handlers may be called in arbitrary order, i.e. last registered handler can be called first, and vice versa.
-
Declaration
boolpostTimeout(ET : Object)(ETevt, uinttimeoutmsecs);Post event to queue. It is safe to call this from non-UI threads.
Discussion
If
is greater than zero, the event will be delayed for at leasttimeoutmsecsmilliseconds. This is in-process one-way (from anything to window) event sending mechanics. It is thread-safe, so it can be used in multi-threaded applications to send, for example, "wake up and repaint" events when thread completed some operation. This will allow to avoid using timer pulse to check events with synchronization, 'cause event handler will be called in UI thread. You can stop guessing which pulse frequency will be enough for your app. Note that events handlers may be called in arbitrary order, i.e. last registered handler can be called first, and vice versa.timeoutmsecs -
Declaration
boolpostEvent(ET : Object)(ETevt);Post event to queue. It is safe to call this from non-UI threads.
Discussion
This is in-process one-way (from anything to window) event sending mechanics. It is thread-safe, so it can be used in multi-threaded applications to send, for example, "wake up and repaint" events when thread completed some operation. This will allow to avoid using timer pulse to check events with synchronization, 'cause event handler will be called in UI thread. You can stop guessing which pulse frequency will be enough for your app. Note that events handlers may be called in arbitrary order, i.e. last registered handler can be called first, and vice versa.
-
-
Declaration
classTimer;A timer that will trigger your function on a given interval.
Discussion
You create a timer with an interval and a callback. It will continue to fire on the interval until it is destroyed. There are currently no one-off timers (instead, just create one and destroy it when it is triggered) nor are there pause/resume functions - the timer must again be destroyed and recreated if you want to pause it. auto timer = new
Timer(50, { it happened!; }); timer.destroy(); Timers can only be expected to fire when the event loop is running.-
Declaration
this(intintervalInMilliseconds, void delegate()onPulse);Create a timer with a callback when it triggers.
-
Declaration
voiddestroy();Stop and
destroythe timer object.
-
-
Declaration
voidgetClipboardText(SimpleWindowclipboardOwner, void delegate(in char[])receiver);this does a delegate because it is actually an async call on X...
Discussion
the
receivermay never be called if the clipboard is empty or unavailable gets plain text from the clipboard -
Declaration
voidsetClipboardText(SimpleWindowclipboardOwner, stringtext);copies some
textto the clipboard -
Declaration
@property AtomGetAtom(string name, bool create = false)(Display*display);Platform specific for X11
-
Declaration
voidsetPrimarySelection(SimpleWindowwindow, stringtext);Asserts ownership of PRIMARY and copies the
textinto a buffer that clients can request later -
Declaration
voidsetSecondarySelection(SimpleWindowwindow, stringtext);Asserts ownership of SECONDARY and copies the
textinto a buffer that clients can request later -
Declaration
voidsetX11Selection(string atomName)(SimpleWindowwindow, stringtext); -
Declaration
voidgetPrimarySelection(SimpleWindowwindow, void delegate(in char[])handler); -
Declaration
voidgetX11Selection(string atomName)(SimpleWindowwindow, void delegate(in char[])handler); -
Declaration
void[]getX11PropertyData(Windowwindow, Atomproperty, Atomtype= AnyPropertyType); -
Declaration
classNotificationAreaIcon: arsd.simpledisplay.CapableOfHandlingNativeEvent; -
Declaration
voidsendSyntheticInput(wstrings);Platform-specific for Windows. Sends a string as key press and release events to the actively focused window (not necessarily your application)
-
Declaration
intregisterHotKey(SimpleWindowwindow, uintmodifiers, uintvk, void delegate()handler);Platform-specific for Windows. Registers a global hotkey. Returns a registration ID.
-
Declaration
voidunregisterHotKey(SimpleWindowwindow, intid);Platform-specific for Windows. Unregisters a key. The
idis the value returned by registerHotKey. -
Declaration
enumRasterOp: int; -
Declaration
enumOpenGlOptions: int;Determines if you want an OpenGL context created on the new window.
Discussion
See more: [#topics-3d|in the 3d topic].
import arsd.simpledisplay; void main() { auto window = new SimpleWindow(500, 500, "OpenGL Test", OpenGlOptions.yes); // Set up the matrix window.setAsCurrentOpenGlContext(); // make this window active // This is called on each frame, we will draw our scene window.redrawOpenGlScene = delegate() { }; window.eventLoop(0); }
-
Declaration
noNo OpenGL context is created
-
Declaration
yesYes, create an OpenGL context
-
Declaration
enumResizablity: int;When you create a SimpleWindow, you can see its resizability to be one of these via the constructor...
-
Declaration
fixedSizethe window cannot be resized
-
Declaration
allowResizingthe window can be resized. The buffer (if there is one) will automatically adjust size, but not stretch the contents. the windowResized delegate will be called so you can respond to the new size yourself.
-
Declaration
automaticallyScaleIfPossibleif possible, your drawing buffer will remain the same size and simply be automatically scaled to the new window size. If this is impossible, it will not allow the user to resize the window at all. Note: window.width and window.height WILL be adjusted, which might throw you off if you draw based on them, so keep track of your expected width and height separately. That way, when it is scaled, things won't be thrown off.
-
-
Declaration
enumTextAlignment: uint;Alignment for . Left, Center, or Right may be combined with VerticalTop, VerticalCenter, or VerticalBottom via bitwise or.
-
Declaration
Left -
Declaration
Center -
Declaration
Right -
Declaration
VerticalTop -
Declaration
VerticalCenter -
Declaration
VerticalBottom
-
-
Declaration
structKeyEvent;Keyboard press and release events
-
Declaration
Keykey;see table below. Always use the symbolic names, even for ASCII characters, since the actual numbers vary across platforms. See [Key]
-
Declaration
uinthardwareCode;A platform and hardware specific code for the key
-
Declaration
boolpressed;trueif the key was justpressed,falseif it was just released. note: released events aren't always sent... -
Declaration
dcharcharacter; -
Declaration
uintmodifierState;see enum [ModifierState]. They are bitwise combined together.
-
Declaration
SimpleWindowwindow;associated Window
-
-
Declaration
enumMouseEventType: int;Type of a [MouseEvent]
-
Declaration
motionThe mouse moved inside the window
-
Declaration
buttonPressedA mouse button was pressed or the wheel was spun
-
Declaration
buttonReleasedA mouse button was released
-
-
Declaration
structMouseEvent;Listen for this on your event listeners if you are interested in mouse action.
-
Declaration
MouseEventTypetype;movement, press, release, double click. See [MouseEventType]
-
Declaration
intx;Current X position of the cursor when the event fired, relative to the upper-left corner of the window, reported in pixels. (0, 0) is the upper left, (window.width - 1, window.height - 1) is the lower right corner of the window.
-
Declaration
inty;Current Y position of the cursor when the event fired.
-
Declaration
intdx;Change in X position since last report
-
Declaration
intdy;Change in Y position since last report
-
Declaration
MouseButtonbutton;See [MouseButton]
-
Declaration
intmodifierState;See [ModifierState]
-
Declaration
SimpleWindowwindow;The
windowin which the event happened.
-
-
Declaration
structPen;This gives a few more options to drawing lines and such
-
Declaration
Colorcolor;the foreground
color -
Declaration
intwidth;widthof the line -
Declaration
Stylestyle;See [Style] FIXME: not implemented
-
Declaration
enumStyle: int;
-
-
Declaration
classImage;Represents an in-memory image in the format that the GUI expects, but with its raw data available to your program.
Discussion
On Windows, this means a device-independent bitmap. On X11, it is an XImage. Drawing an image to screen is not necessarily fast, but applying algorithms to draw to the image itself should be fast. An
is also the first step in loading and displaying images loaded from files. If you intend to draw an image to screen several times, you will want to convert it into a [Sprite]. Drawing pixels on the image may be simple, using theImageopIndexAssignfunction, but you can also often get a fair amount of speedup by getting the raw data format and writing some custom code. FIXME INSERT EXAMPLES HERE-
Declaration
this(intwidth, intheight); -
Declaration
this(Sizesize); -
Declaration
const pure nothrow @system intoffsetForTopLeftPixel();if you do the math yourself you might be able to optimize it. Call these functions only once and cache the value.
-
Declaration
const pure nothrow @system intoffsetForPixel(intx, inty);if you do the math yourself you might be able to optimize it. Call these functions only once and cache the value.
-
Declaration
const pure nothrow @system intadjustmentForNextLine();if you do the math yourself you might be able to optimize it. Call these functions only once and cache the value.
-
Declaration
const pure nothrow @system intredByteOffset();once you have the position of a pixel, use these to get to the proper color
Discussion
if you do the math yourself you might be able to optimize it. Call these functions only once and cache the value.
-
Declaration
const pure nothrow @system intgreenByteOffset();if you do the math yourself you might be able to optimize it. Call these functions only once and cache the value.
-
Declaration
const pure nothrow @system intblueByteOffset();if you do the math yourself you might be able to optimize it. Call these functions only once and cache the value.
-
Declaration
final voidputPixel(intx, inty, Colorc); -
Declaration
final ColorgetPixel(intx, inty); -
Declaration
final voidopIndexAssign(Colorc, intx, inty); -
Declaration
TrueColorImagetoTrueColorImage(); -
Declaration
static ImagefromMemoryImage(MemoryImagei); -
Declaration
ubyte[]getRgbaBytes(ubyte[]where= null);this is here for interop with arsd.image.
wherecan be a TrueColorImage's data memberDiscussion
if you pass in a buffer, it will put it right there. length must be width*height*4 already if you pass
null, it will allocate a new one. -
Declaration
voidsetRgbaBytes(in ubyte[]from);this is here for interop with arsd.image.
fromcan be a TrueColorImage's data member -
Declaration
ubyte*getDataPointer();warning: this is not portable across platforms because the data format can change
-
Declaration
final const pure nothrow @safe intbytesPerLine();for use with getDataPointer
-
Declaration
final const pure nothrow @safe intbytesPerPixel();for use with getDataPointer
-
Declaration
immutable intwidth; -
Declaration
immutable intheight;
-
-
Declaration
voiddisplayImage(Imageimage, SimpleWindowwin= null);A convenience function to pop up a window displaying the
image.Discussion
If you pass a
win, it will draw theimagein it. Otherwise, it will create a window with the size of theimageand run its event loop, closing when a key is pressed. -
Declaration
structScreenPainter;The 2D drawing proxy.
Discussion
Most functions use the outlineColor instead of taking a color themselves.
ScreenPainteris reference counted and draws its buffer to the screen when its final reference goes out of scope.-
Declaration
intfontHeight(); -
Declaration
@property voidpen(Penp); -
Declaration
@property voidoutlineColor(Colorc); -
Declaration
@property voidfillColor(Colorc); -
Declaration
@property voidrasterOp(RasterOpop); -
Declaration
voidscrollArea(PointupperLeft, intwidth, intheight, intdx, intdy);Scrolls the contents in the bounding rectangle by
dx,dy. Positivedxmeans scroll left (make space available at the right), positivedymeans scroll up (make space available at the bottom) -
Declaration
voidclear(); -
Declaration
voiddrawPixmap(Sprites, PointupperLeft); -
Declaration
voiddrawImage(PointupperLeft, Imagei, PointupperLeftOfImage= Point(0, 0), intw= 0, inth= 0); -
Declaration
SizetextSize(stringtext); -
Declaration
voiddrawText(PointupperLeft, in char[]text, PointlowerRight= Point(0, 0), uintalignment= 0); -
Declaration
voiddrawPixel(Pointwhere);Drawing an individual pixel is slow. Avoid it if possible.
-
Declaration
voiddrawLine(Pointstarting, Pointending);Draws a pen using the current pen / outlineColor
-
Declaration
voiddrawRectangle(PointupperLeft, intwidth, intheight);Draws a rectangle using the current pen/outline color for the border and brush/fill color for the insides
Discussion
The outer lines, inclusive of x = 0, y = 0, x =
width- 1, and y =height- 1 are drawn with the outlineColor The rest of the pixels are drawn with the fillColor. If fillColor is transparent, those pixels are not drawn. -
Declaration
voiddrawEllipse(PointupperLeft, PointlowerRight);Arguments are the points of the bounding rectangle
-
Declaration
voiddrawPolygon(Point[]vertexes);.
-
-
Declaration
classSprite;Sprites are optimized for fast drawing on the screen, but slow for direct pixel access. They are best for drawing a relatively unchanging image repeatedly on the screen.
Discussion
On X11, this corresponds to an
XPixmap. On Windows, it still uses a bitmap, though I'm not sure that's ideal and the implementation might change. You create one by giving a window and an image. It optimizes for that window, and copies the image into it to use as the initial picture. Creating a sprite can be quite slow (especially over a network connection) so you should do it as little as possible and just hold on to your sprite handles after making them. simpledisplay does try to do its best though, using the XSHM extension if available, but you should still write your code as if it will always be slow. Then you can usesprite.drawAt(painter, point);to draw it, which should be a fast operation - much faster than drawing the Image itself every time.represents a scarce resource which should be freed when you are done with it. Use theSpritedisposemethod to do this. Do not use aafter it has been disposed. If you are unsure about this, don't take chances, just let the garbage collector do it for you. But ideally, you can manage its lifetime more efficiently.SpriteFIXME: you are supposed to be able to draw on these similarly to on windows. ScreenPainter needs to be refactored to allow that though. So until that is done, consider a
to have const contents.Sprite-
Declaration
this(SimpleWindowwin, Imagei);Makes a sprite based on the image with the initial contents from the Image
-
Declaration
voiddrawAt(ScreenPainterpainter, Pointwhere);Draws the image on the specified
painterat the specified point. The point is the upper-left pointwherethe image will be drawn. -
Declaration
voiddispose();Call this when you're ready to get rid of it
-
Declaration
final @property intwidth(); -
Declaration
final @property intheight();
-
-
Declaration
voidflushGui();Flushes any pending gui buffers. Necessary if you are using with_eventloop with X - flush after you create your windows but before you call loop()
-
Declaration
enumModifierState: uint;State of keys on mouse events, especially motion.
Discussion
Do not trust the actual integer values in this, they are platform-specific. Always use the names.
-
Declaration
shift -
Declaration
capsLock -
Declaration
ctrl -
Declaration
alt -
Declaration
numLock -
Declaration
windows -
Declaration
leftButtonDown -
Declaration
middleButtonDown -
Declaration
rightButtonDown
-
-
Declaration
enumMouseButton: int; -
Declaration
enumKey: int;Do not trust the numeric values as they are platform-specific. Always use the symbolic name.
-
Declaration
Escape -
Declaration
F1 -
Declaration
F2 -
Declaration
F3 -
Declaration
F4 -
Declaration
F5 -
Declaration
F6 -
Declaration
F7 -
Declaration
F8 -
Declaration
F9 -
Declaration
F10 -
Declaration
F11 -
Declaration
F12 -
Declaration
PrintScreen -
Declaration
ScrollLock -
Declaration
Pause -
Declaration
GraveThe ` ~ key
-
Declaration
N1Number key atop the keyboard
-
Declaration
N2 -
Declaration
N3 -
Declaration
N4 -
Declaration
N5 -
Declaration
N6 -
Declaration
N7 -
Declaration
N8 -
Declaration
N9 -
Declaration
N0 -
Declaration
Dash -
Declaration
Equals -
Declaration
BackslashThe \ | key
-
Declaration
Backspace -
Declaration
Insert -
Declaration
Home -
Declaration
PageUp -
Declaration
Delete -
Declaration
End -
Declaration
PageDown -
Declaration
Up -
Declaration
Down -
Declaration
Left -
Declaration
Right -
Declaration
Tab -
Declaration
Q -
Declaration
W -
Declaration
E -
Declaration
R -
Declaration
T -
Declaration
Y -
Declaration
U -
Declaration
I -
Declaration
O -
Declaration
P -
Declaration
LeftBracketthe [ { key
-
Declaration
RightBracketthe ] } key
-
Declaration
CapsLock -
Declaration
A -
Declaration
S -
Declaration
D -
Declaration
F -
Declaration
G -
Declaration
H -
Declaration
J -
Declaration
K -
Declaration
L -
Declaration
Semicolon -
Declaration
Apostrophe -
Declaration
Enter -
Declaration
Shift -
Declaration
Z -
Declaration
X -
Declaration
C -
Declaration
V -
Declaration
B -
Declaration
N -
Declaration
M -
Declaration
Comma -
Declaration
Period -
Declaration
Slashthe / ? key
-
Declaration
Shift_rNote: this isn't sent on all computers, sometimes it just sends Shift, so don't rely on it. If it is supported though, it is the right Shift key, as opposed to the left Shift key
-
Declaration
Ctrl -
Declaration
Windows -
Declaration
Alt -
Declaration
Space -
Declaration
Alt_rditto of shift_r
-
Declaration
Windows_r -
Declaration
Menu -
Declaration
Ctrl_r -
Declaration
NumLock -
Declaration
DivideThe / key on the number pad
-
Declaration
MultiplyThe * key on the number pad
-
Declaration
MinusThe - key on the number pad
-
Declaration
PlusThe + key on the number pad
-
Declaration
PadEnterNumberpad enter key
-
Declaration
Pad1Numberpad keys
-
Declaration
Pad2 -
Declaration
Pad3 -
Declaration
Pad4 -
Declaration
Pad5 -
Declaration
Pad6 -
Declaration
Pad7 -
Declaration
Pad8 -
Declaration
Pad9 -
Declaration
Pad0 -
Declaration
PadDot
-
-
Declaration
stringxfontstr;This is the default font used. You might change this before doing anything else with
Discussion
the library if you want to try something else. Surround that in
static if(UsingSimpledisplayX11)for cross-platform compatibility. -
Declaration
classXDisplayConnection; -
Declaration
booldoXNextEvent(Display*display);Platform-specific, you might use it when doing a custom event loop
-
Declaration
voiddemandAttention(SimpleWindowwindow, boolneeds= true);X-specific
-
Declaration
TrueColorImagegetWindowNetWmIcon(Windowwindow);X-specific