When you create a SimpleWindow, you can see its resizability to be one of these via the constructor...
A simple handler for making your window accept drops of files, issued to you as file names.
A generic helper base class for making a drop handler with a preference list of custom types. This is the base for TextDropHandler and FilesDropHandler and you can use it for your own droppers too.
Global hotkey handler. Simpledisplay will usually create one for you, but if you want to use subclassing * instead of delegates, you can subclass this, and override doHandle() method.
Global hotkey manager. It contains static methods to manage global hotkeys.
Represents an in-memory image in the format that the GUI expects, but with its raw data available to your program.
Represents a mouse cursor (aka the mouse pointer, the image seen on screen that indicates where the mouse is pointing). See GenericCursor.
A static container of experimental types and value constructors for opengl 3+ shaders.
Represents a font loaded off the operating system or the X server.
Lets you add files to the event loop for reading. Use at your own risk.
The flagship window class.
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.
A simple handler for making your window accept drops of plain text.
Lets you add HANDLEs to the event loop. Not meant to be used for async I/O per se, but for other handles (it can only handle a few handles at a time.) Only works on certain types of handles! See: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex
Platform-specific for X11. A singleton class (well, all its methods are actually static... so more like a namespace) wrapping a Display*.
Blocking mode for event loop calls associated with a window instance.
Window corner visuals preference
Note that there is no exact appearance guaranteed for any of these items; it may change appearance on different operating systems or future simpledisplay versions.
Do not trust the numeric values as they are platform-specific. Always use the symbolic name.
Do not trust the numeric values as they are platform-specific. Always use the symbolic name.
State of keys on mouse events, especially motion.
The names assume a right-handed mouse. These are bitwise combined on the events that use them.
Corresponds to the values found in MouseEvent.buttonLinear, being equal to core.bitop.bsf(button) + 1
Type of a MouseEvent.
Determines if you want an OpenGL context created on the new window.
ScreenPainter operations can use different operations to combine the color with the color on screen.
When you create a SimpleWindow, you can see its resizability to be one of these via the constructor...
Alignment for ScreenPainter.drawText. Left, Center, or Right may be combined with VerticalTop, VerticalCenter, or VerticalBottom via bitwise or.
After selecting a type from WindowTypes, you may further customize its behavior by setting one or more of these flags.
When creating a window, you can pass a type to SimpleWindow's constructor, then further customize the window by changing WindowFlags.
Loads a true type font using arsd.ttf that can be drawn as images on windows through a ScreenPainter. That module must be compiled in if you choose to use this function.
Returns the custom scaling factor read out of environment["ARSD_SCALING_FACTOR"].
X-specific. Use SimpleWindow.requestAttention instead for most cases.
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.
Platform-specific, you might use it when doing a custom event loop.
Flushes any pending gui buffers. Necessary if you are using with_eventloop with X - flush after you create your windows but before you call arsd.eventloop.loop.
Platform-specific for X11 - gets atom names as a string.
this does a delegate because it is actually an async call on X... the receiver may never be called if the clipboard is empty or unavailable gets image from the clipboard.
this does a delegate because it is actually an async call on X... the receiver may never be called if the clipboard is empty or unavailable gets plain text from the clipboard.
Returns the logical DPI of the default monitor. [0] is width, [1] is height (they are usually the same though). You may wish to round the numbers off. This isn't necessarily related to the physical side of the screen; it is associated with a user-defined scaling factor.
X-specific
Gets the image on the clipboard, if there is one. Added July 2020. only supports bmps. using this function will import arsd.bmp.
Returns true if a gui thread exists, that is, a thread running the simpledisplay.d event loop. All windows must be exclusively created and managed by a single thread.
Platform-specific for Windows. Registers a global hotkey. Returns a registration ID. See GlobalHotkeyManager for Linux. Maybe some day I will merge these.
Runs the given code in the GUI thread when its event loop is available, blocking until it completes. This allows you to create and manipulate windows from another thread without invoking undefined behavior.
Function to help temporarily print debugging info. It will bypass any stdout/err redirection and go to the controlling tty or console (attaching to the parent and/or allocating one as needed on Windows. Please note it may overwrite output from other programs in the parent and the allocated one will not survive if your program crashes. Use the fileOverride to print to a log file instead if you are in one of those situations).
Set window class name for all following new SimpleWindow() calls.
Get current window class name.
See SyntheticInput.sendSyntheticInput instead for cross-platform applications.
Copies some text to the clipboard.
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.
Asserts ownership of PRIMARY and copies the text into a buffer that clients can request later.
Asserts ownership of SECONDARY and copies the text into a buffer that clients can request later.
The after delegate is called after a client requests the UTF8_STRING thing. it is a mega-hack right now! (note to self july 2020... why did i do that?!)
Returns true if this thread is either running or set to be running the simpledisplay.d gui core event loop because it owns windows.
Call XFreePixmap on the return value.
Platform-specific for X11. Traps errors for the duration of dg. Avoid calling this from inside a call to this.
Implementation used by SimpleWindow.takeScreenshot.
Platform-specific for Windows. Unregisters a key. The id is the value returned by registerHotKey.
Used internal to dispatch events to various classes.
Interface to describe data being dragged. See also draggable helper function.
An interface representing a font that is drawn with custom facilities.
Interface with the common functionality for font measurements between OperatingSystemFont and DrawableFont.
Adds the necessary pragmas to your application to use the Windows gui subsystem. If you mix this in above your main function, you no longer need to use the linker flags explicitly. It does the necessary version blocks for various compilers and runtimes.
Sets the application name.
Platform-specific for X11.
Set to true to allow creating OpenGL context with lower version than requested instead of throwing. If fallback was activated (or legacy OpenGL was requested), openGLContextFallbackActivated() will return true.
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").
After creating OpenGL window, you can check this to see if you got only "legacy" OpenGL context.
Magic pseudo-window for just posting events to a global queue.
This is the default font used. You might change this before doing anything else with the library if you want to try something else. Surround that in static if(UsingSimpledisplayX11) for cross-platform compatibility.
A matrix for simple uses that easily integrates with OpenGlShader.
An opaque structure representing dropped data. It contains private, platform-specific data that your drop function should simply forward.
If you want to get more control over the event loop, you can use this.
You get one by GenericCursor.SomeTime. See GenericCursorType for a list of types.
Keyboard press and release events.
Listen for this on your event listeners if you are interested in mouse action.
This gives a few more options to drawing lines and such
The 2D drawing proxy. You acquire one of these with SimpleWindow.draw rather than constructing it directly. Then, it is reference counted so you can pass it at around and when the last ref goes out of scope, the buffered drawing activities are all carried out.
Allows for sending synthetic input to the X server via the Xtst extension or on Windows using SendInput.
If you have to get down and dirty with implementation details, this helps figure out if Cocoa is available you can static if(UsingSimpledisplayCocoa) ... more reliably than version() because version is module-local.
If you have to get down and dirty with implementation details, this helps figure out if Windows is available you can static if(UsingSimpledisplayWindows) ... more reliably than version() because version is module-local.
If you have to get down and dirty with implementation details, this helps figure out if X is available you can static if(UsingSimpledisplayX11) ... more reliably than version() because version is module-local.
Double click timeout. X only, you probably shouldn't change this.
simpledisplay.d does not have any dependencies outside the operating system and color.d, so it should just work most the time, but there are a few caveats on some systems:
On Win32, you can pass -L/subsystem:windows if you don't want a console to be automatically allocated.
Please note when compiling on Win64, you need to explicitly list -Lgdi32.lib -Luser32.lib on the build command. If you want the Windows subsystem too, use -L/subsystem:windows -L/entry:mainCRTStartup.
If using ldc instead of dmd, use -L/entry:wmainCRTStartup instead of mainCRTStartup; note the "w".
I provided a mixin EnableWindowsSubsystem; helper to do those linker flags for you, but you still need to use dmd -m32mscoff or -m64 (which dub does by default too fyi). See EnableWindowsSubsystem for more information.
On Mac, when compiling with X11, you need XQuartz and -L-L/usr/X11R6/lib passed to dmd. If using the Cocoa implementation on Mac, you need to pass -L-framework -LCocoa to dmd. For OpenGL, add -L-framework -LOpenGL to the build command.
On Ubuntu, you might need to install X11 development libraries to successfully link.
$ sudo apt-get install libglc-dev $ sudo apt-get install libx11-dev
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.
To do otherwise is undefined behavior and has no cross platform guarantees.
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 simpledisplay.d and color.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!
simpledisplay can be used with core.thread.Fiber, but be warned many of the functions can use a significant amount of stack space. I recommend at least 64 KB stack for each fiber (just set through the second argument to Fiber's constructor).
The SimpleWindow class is 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.
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 */ } ); }
On Linux, the event loop is implemented with the 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 (deprecated) 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.
You can also run the event loop independently of a window, with EventLoop.get.run, though since it will automatically terminate when there are no open windows, you will want to have one anyway.
Notification area icons are currently implemented on X11 and Windows. On X11, it defaults to using libnotify to show bubbles, if available, and will do a custom bubble window if not. You can version=without_libnotify to avoid this run-time dependency, if you like.
See the NotificationAreaIcon class.
There are event handlers for low-level keyboard and mouse events, and higher level handlers for character events.
See SimpleWindow.handleCharEvent, SimpleWindow.handleKeyEvent, SimpleWindow.handleMouseEvent.
To draw on your window, use the SimpleWindow.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 }
Painting is done based on two color properties, a pen and a brush.
At this time, the 2d drawing does not support alpha blending, except for the Sprite class. If you need that, use a 2d OpenGL context instead.
FIXME Add example of 2d opengl drawing here.
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 window.redrawOpenGlScene to a delegate which draws your frame.
To force a redraw of the scene, call window.redrawOpenGlSceneNow() or to queue a redraw after processing the next batch of pending events, use window.redrawOpenGlSceneSoon.
simpledisplay supports both old-style glBegin and newer-style shader-based code all through its built-in bindings. See the next section of the docs to see a shader-based program.
This example program will draw a rectangle on your window using old-style OpenGL with a pulsating color:
import arsd.simpledisplay; void main() { auto window = new SimpleWindow(800, 600, "opengl 1", OpenGlOptions.yes, Resizability.allowResizing); float otherColor = 0.0; float colorDelta = 0.05; window.redrawOpenGlScene = delegate() { glLoadIdentity(); glBegin(GL_QUADS); glColor3f(1.0, otherColor, 0); glVertex3f(-0.8, -0.8, 0); glColor3f(1.0, otherColor, 1.0); glVertex3f(0.8, -0.8, 0); glColor3f(0, 1.0, otherColor); glVertex3f(0.8, 0.8, 0); glColor3f(otherColor, 0, 1.0); glVertex3f(-0.8, 0.8, 0); glEnd(); }; window.eventLoop(50, () { otherColor += colorDelta; if(otherColor > 1.0) { otherColor = 1.0; colorDelta = -0.05; } if(otherColor < 0) { otherColor = 0; colorDelta = 0.05; } // at the end of the timer, we have to request a redraw // or we won't see the changes. window.redrawOpenGlSceneSoon(); }); }
My arsd.game module has some helpers for using old-style opengl to make 2D windows too. See: arsd.game.create2dWindow.
simpledisplay's opengl support, by default, is for "legacy" opengl. To use "modern" functions, you must opt-into them with a little more setup. But the library provides helpers for this too.
This example program shows how you can set up a shader to draw a rectangle:
1 import arsd.simpledisplay; 2 3 // based on https://learnopengl.com/Getting-started/Hello-Triangle 4 5 void main() { 6 // First thing we do, before creating the window, is declare what version we want. 7 setOpenGLContextVersion(3, 3); 8 // turning off legacy compat is required to use version 3.3 and newer 9 openGLContextCompatible = false; 10 11 uint VAO; 12 OpenGlShader shader; 13 14 // then we can create the window. 15 auto window = new SimpleWindow(800, 600, "opengl 3", OpenGlOptions.yes, Resizability.allowResizing); 16 17 // additional setup needs to be done when it is visible, simpledisplay offers a property 18 // for exactly that: 19 window.visibleForTheFirstTime = delegate() { 20 // now with the window loaded, we can start loading the modern opengl functions. 21 22 // you MUST set the context first. 23 window.setAsCurrentOpenGlContext; 24 // then load the remainder of the library 25 gl3.loadDynamicLibrary(); 26 27 // now you can create the shaders, etc. 28 shader = new OpenGlShader( 29 OpenGlShader.Source(GL_VERTEX_SHADER, ` 30 #version 330 core 31 layout (location = 0) in vec3 aPos; 32 void main() { 33 gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); 34 } 35 `), 36 OpenGlShader.Source(GL_FRAGMENT_SHADER, ` 37 #version 330 core 38 out vec4 FragColor; 39 uniform vec4 mycolor; 40 void main() { 41 FragColor = mycolor; 42 } 43 `), 44 ); 45 46 // and do whatever other setup you want. 47 48 float[] vertices = [ 49 0.5f, 0.5f, 0.0f, // top right 50 0.5f, -0.5f, 0.0f, // bottom right 51 -0.5f, -0.5f, 0.0f, // bottom left 52 -0.5f, 0.5f, 0.0f // top left 53 ]; 54 uint[] indices = [ // note that we start from 0! 55 0, 1, 3, // first Triangle 56 1, 2, 3 // second Triangle 57 ]; 58 uint VBO, EBO; 59 glGenVertexArrays(1, &VAO); 60 // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). 61 glBindVertexArray(VAO); 62 63 glGenBuffers(1, &VBO); 64 glGenBuffers(1, &EBO); 65 66 glBindBuffer(GL_ARRAY_BUFFER, VBO); 67 glBufferDataSlice(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW); 68 69 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 70 glBufferDataSlice(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW); 71 72 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * float.sizeof, null); 73 glEnableVertexAttribArray(0); 74 75 // the library will set the initial viewport and trigger our first draw, 76 // so these next two lines are NOT needed. they are just here as comments 77 // to show what would happen next. 78 79 // glViewport(0, 0, window.width, window.height); 80 // window.redrawOpenGlSceneNow(); 81 }; 82 83 // this delegate is called any time the window needs to be redrawn or if you call `window.redrawOpenGlSceneNow;` 84 // it is our render method. 85 window.redrawOpenGlScene = delegate() { 86 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 87 glClear(GL_COLOR_BUFFER_BIT); 88 89 glUseProgram(shader.shaderProgram); 90 91 // the shader helper class has methods to set uniforms too 92 shader.uniforms.mycolor.opAssign(1.0, 1.0, 0, 1.0); 93 94 glBindVertexArray(VAO); 95 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, null); 96 }; 97 98 window.eventLoop(0); 99 }
This program only draws the image once because that's all that is necessary, since it is static. If you want to do animation, you might set a pulse timer (which would be a fixed max fps, not necessarily consistent) or use a render loop in a separate thread.
See a couple examples ported from GLFW to simpledisplay using the erupted vulkan bindings:
https://github.com/adamdruppe/VulkanizeDSdpy
https://github.com/adamdruppe/VulkanSdpyDemo/tree/demo
You can also load PNG images using arsd.png.
// 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); }
Compile with dmd example.d simpledisplay.d png.d.
If you find an image file which is a valid png that arsd.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.
Sprite is also the only facility that currently supports alpha blending without using OpenGL .
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.
See enableDragAndDrop and draggable.
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))); }); }
The 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); }
Timers are currently only implemented on Windows, using 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.
On X11, if you set an environment variable, ARSD_SCALING_FACTOR, you can control the per-monitor DPI scaling returned to the application. The format is ARSD_SCALING_FACTOR=2;1, for example, to set 2x scaling on your first monitor and 1x scaling on your second monitor. Support for this was added on March 22, 2022, the dub 10.7 release.
Out of the box, simpledisplay might not work as expected in combination with apitrace. However it can be instructed to specifically load the GL/GLX wrapper libraries provided by apitrace instead of the system libraries. This should restore the lost functionality.
There are multiple ways to enable a simpledisplay app to locate the wrapper libraries.
One way to achieved this is by pointing the LD_LIBRARY_PATH environment variable to the directory containing those wrappers.
LD_LIBRARY_PATH=/path/to/apitrace/wrappers:$LD_LIBRARY_PATH ./myapp # e.g. LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/apitrace/wrappers:$LD_LIBRARY_PATH ./myapp
Alternatively, the simpledisplay app can also be launched via apitrace.
apitrace trace -a gl ./myapp
Another way that seems to work is to preload glxtrace.so through LD_PRELOAD.
LD_PRELOAD=/path/to/apitrace/wrappers/glxtrace.so ./myapp
You can add icons or manifest files to your exe using a resource file.
To create a Windows .ico file, use the gimp or something. I'll write a helper program later.
Create yourapp.rc:
1 ICON filename.ico CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "YourApp.exe.manifest"
And yourapp.exe.manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="CompanyName.ProductName.YourApplication" type="win32" /> <description>Your application description here.</description> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- old style --> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> <!-- new style --> <!-- Un-comment the line below to enable GDI-scaling in this project. This will enable text --> <!-- to render crisply in DPI-unaware contexts --> <!--<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>--> </windowsSettings> </application> </assembly>
You can also just distribute yourapp.exe.manifest as a separate file alongside yourapp.exe, or link it in to the exe with linker command lines /manifest:embed and /manifestinput:yourfile.exe.manifest.
Doing this lets you opt into various new things since Windows XP.
See: https://docs.microsoft.com/en-us/windows/win32/SbsCs/application-manifests
simpledisplay has a lot of symbols and more are liable to be added without notice, since it contains its own bindings as needed to accomplish its goals. Some of these may conflict with other bindings you use. If so, you can use a static import in D, possibly combined with a selective import:
static import sdpy = arsd.simpledisplay; import arsd.simpledisplay : SimpleWindow; void main() { auto window = new SimpleWindow(); sdpy.EventLoop.get.run(); }
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.
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), "Event example - simpledisplay.d"); 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)); } ); }
If you are interested in more game writing with D, check out my gamehelpers.d which builds upon simpledisplay, and its other stand-alone support modules, simpleaudio.d and joystick.d, too.
Copyright Adam D. Ruppe, 2011-2021. Released under the Boost Software License.
Building documentation: use my adrdox generator, dub run adrdox.
Initial release in April 2011.
simpledisplay was stand alone until about 2015. It then added a dependency on arsd.color and changed its name to arsd.simpledisplay.
On March 4, 2023 (dub v11.0), it started importing arsd.core as well, making that a build-time requirement.
On October 5, 2024, apitrace support was added for Linux targets.
simpledisplay.d (often abbreviated to "sdpy") 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.
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 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.