Ok, to start with, I've been working on the GUI for my OS as a little side project to the main kernel, and I was wondering what sort of GUI systems people are using. Meaning, communication between the "Server" (the program that does the actual drawing) and the "client" (the application)
Personally, I was quite a fan of the message based WinAPI approach, where each window has its own message handler, and a library function distributes messages out to these functions. The Windows API is a medium level GUI system, providing the concept of a "window", and callbacks to draw it.
Then there is the X approach, which again provides "windows", but at a lower level, just really providing a canvas on which to draw. To me, this seems too low level to be effective over most non-local networks (and even then, it is rather traffic intensive)
My current GUI design provides a server-level widget library, and is designed to implement a form of GUI "bytecode", allowing parts of the client application to be uploaded to the server for improved responsiveness (the code can be used to render custom controls without requiring many IPC/network messages, hence improving latency)
What do you guys think of this design and/or what do you use? Any suggestions for my design?
GUI Systems - Comparison and Discussion
- thepowersgang
- Member
- Posts: 734
- Joined: Tue Dec 25, 2007 6:03 am
- Libera.chat IRC: thePowersGang
- Location: Perth, Western Australia
- Contact:
GUI Systems - Comparison and Discussion
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
- NickJohnson
- Member
- Posts: 1249
- Joined: Tue Mar 24, 2009 8:11 pm
- Location: Sunnyvale, California
Re: GUI Systems - Comparison and Discussion
I haven't started on my GUI yet, but it seems like the best design would be a hybrid, layered design, that has an X-like engine for low level windowing, and a closely-coupled intermediate server (or set of servers) to draw/cache widgets and stuff and to give a better interface. You would create a low-level window and bind a high-level window to it's canvas, then instruct the high-level window (in nicer, more compact commands, like on Windows) to do the drawing on the low-level window. This would provide good network performance and good flexibility (i.e. good separation of mechanism and policy,) although if you don't need to change the GUI policy that much - Windows doesn't - it's probably overkill.
Re: GUI Systems - Comparison and Discussion
I've heard people talk about the "well-designed GUI API" under SkyOS, that I want to look into at some point. But GUIs are still a year out for my OS -- I've got a lot of driver and kernel API stuff to work through before I get anywhere near the GUI stage. Mostly what I want to do is take the nicest existing GUI API I can find and simplify it.
Re: GUI Systems - Comparison and Discussion
Hi,
For my design...
The OS uses messaging for IPC. Messages can be large (e.g up to 4 MiB), and each message has a 32-bit "message ID". I have a standardised "user interface" protocol that's built on top of messaging; where the highest bit of the message ID determines direction (e.g. message IDs 0x00000000 to 0x7FFFFFFF are intended to be sent from applications up to device drivers, while message IDs 0x80000000 to 0xFFFFFFFF are intended to be sent from device drivers back down to applications). The next highest bits of the message ID are used to signify categories. For example, message IDs 0x01000000 to 0x01FFFFFF (and 0x81000000 to 0x81FFFFFF) used for keyboard, 0x02000000 to 0x02FFFFFF (and 0x82000000 to 0x82FFFFFF) used for mouse/pointer, etc.
The "user interface" protocol has clients and servers. For example, a GUI would be a server and applications would be its clients. However, the GUI itself is also a client, and would use the "user interface" protocol to talk to a "user manager". The "user manager" is a layer that routes messages to/from the device drivers themselves. For example, an administrator might attach 2 keyboard drivers to the same "user manager", and the "user manager" would forward messages from either keyboard driver to its client, and would send any (keyboard related) messages from its client to both keyboard drivers. Device drivers all use a subset of the "user interface" protocol (for e.g. a keyboard driver generates messages with IDs 0x01000000 to 0x01FFFFFF and understands messages it receives that have message IDs 0x81000000 to 0x81FFFFFF). In this way the standardised "user interface" protocol is also a standardised device driver interface.
A GUI may have "none or more" clients (e.g. applications), where it merges messages from all clients/applications with it's own messages and sends them to the "user manager", and receives messages from the "user manager" and forwards them to one or more applications.
Because everything relies on the same standardised "user interface" protocol, it's easy to mix and match. A full screen application could talk directly to the "user manager" with no GUI running at all. GUIs could be nested (e.g. applications running in a GUI, which is running in a window of another GUI). You could insert a "virtual desktops" layer between the "user manager" and the GUI/s. Something like a debugger could also act as a server, so that when you're debugging an application that application is shown inside the debugger's window. Also, things like widgets would be implemented as stand-alone processes. For example, an "open file" dialog box would be (mostly) just a client process that talks the same "user interface" protocol, where its server might be a normal application (e.g. a word processor or something).
Of course this means that it'd be possible to run each widget as a "stand alone" application (e.g. inside a debugger). Message IDs 0x00000000 to 0x00FFFFFF (and 0x80000000 to 0x80FFFFFF) would be designated as "user defined", and used by widgets for communication (e.g. for the "open file" dialog box, maybe the widget sends the name of the selected file back to its server as message ID 0x00000008). In general, if anything receives a message it doesn't understand it ignores the message, so when a widget is running as a stand-alone application (e.g. inside a debugger) the server would ignore the "user defined" messages.
The "user manager" is also responsible for login. Before a user logs in the "user manager" has no client. When a user does log in the "user manager" checks the configuration for that user and starts a process (which becomes the "user manager's" client). Different users can have different configuration, so one user might log in and "fancy GUI" will be started, a different user might log in and "simple GUI" might be started. The administrator might configure things so that if "guest" logs in, an internet browser is started (so that the browser runs as a full-screen application, and "guest" doesn't get a GUI or anything). If the "user manager's" client disconnects (e.g. is terminated for any reason) then the "user manager" returns to the log in prompt.
For the standardised "user interface" protocol itself, for most of the devices (keyboard, mouse, joystick, touchpad, touchscreen, etc) it's easy to guess and there's no need for me to describe it.
For video, the clients create lists of commands that describe 3D scenes (where a scene can include other scenes) and sends the list/s to their server (which can modify the lists of commands if necessary and add it's own list/s of commands, before sending them to its server, etc). The commands in the list use virtual (x, y, z) co-ordinates and a standardised representation for colours (for e.g. "draw cube from (x1, y1, z1) to (x2, y2, z2) using <colour>"), so that the only piece of software that knows/cares which video mode is being used is the video driver/s themselves. A video driver is responsible for all drawing (but relies on a separate "font engine" process to convert UTF-8 strings into alpha-only bitmaps). A good video driver would cache scenes (so they're only redrawn if/when they change) and use hardware acceleration wherever possible to draw the scenes; and would automatically increase/decrease graphics quality to try to maintain a certain refresh rate.
Of course there'd probably also be libraries to handle half the work; so that (for e.g.) a normal application programmer can use functions like "draw cube()" (to add a command to a list of video commands) and "update()" (to send the a list of video commands), and "create_open_file_dialog()" to spawn a widget process.
Cheers,
Brendan
For my design...
The OS uses messaging for IPC. Messages can be large (e.g up to 4 MiB), and each message has a 32-bit "message ID". I have a standardised "user interface" protocol that's built on top of messaging; where the highest bit of the message ID determines direction (e.g. message IDs 0x00000000 to 0x7FFFFFFF are intended to be sent from applications up to device drivers, while message IDs 0x80000000 to 0xFFFFFFFF are intended to be sent from device drivers back down to applications). The next highest bits of the message ID are used to signify categories. For example, message IDs 0x01000000 to 0x01FFFFFF (and 0x81000000 to 0x81FFFFFF) used for keyboard, 0x02000000 to 0x02FFFFFF (and 0x82000000 to 0x82FFFFFF) used for mouse/pointer, etc.
The "user interface" protocol has clients and servers. For example, a GUI would be a server and applications would be its clients. However, the GUI itself is also a client, and would use the "user interface" protocol to talk to a "user manager". The "user manager" is a layer that routes messages to/from the device drivers themselves. For example, an administrator might attach 2 keyboard drivers to the same "user manager", and the "user manager" would forward messages from either keyboard driver to its client, and would send any (keyboard related) messages from its client to both keyboard drivers. Device drivers all use a subset of the "user interface" protocol (for e.g. a keyboard driver generates messages with IDs 0x01000000 to 0x01FFFFFF and understands messages it receives that have message IDs 0x81000000 to 0x81FFFFFF). In this way the standardised "user interface" protocol is also a standardised device driver interface.
A GUI may have "none or more" clients (e.g. applications), where it merges messages from all clients/applications with it's own messages and sends them to the "user manager", and receives messages from the "user manager" and forwards them to one or more applications.
Because everything relies on the same standardised "user interface" protocol, it's easy to mix and match. A full screen application could talk directly to the "user manager" with no GUI running at all. GUIs could be nested (e.g. applications running in a GUI, which is running in a window of another GUI). You could insert a "virtual desktops" layer between the "user manager" and the GUI/s. Something like a debugger could also act as a server, so that when you're debugging an application that application is shown inside the debugger's window. Also, things like widgets would be implemented as stand-alone processes. For example, an "open file" dialog box would be (mostly) just a client process that talks the same "user interface" protocol, where its server might be a normal application (e.g. a word processor or something).
Of course this means that it'd be possible to run each widget as a "stand alone" application (e.g. inside a debugger). Message IDs 0x00000000 to 0x00FFFFFF (and 0x80000000 to 0x80FFFFFF) would be designated as "user defined", and used by widgets for communication (e.g. for the "open file" dialog box, maybe the widget sends the name of the selected file back to its server as message ID 0x00000008). In general, if anything receives a message it doesn't understand it ignores the message, so when a widget is running as a stand-alone application (e.g. inside a debugger) the server would ignore the "user defined" messages.
The "user manager" is also responsible for login. Before a user logs in the "user manager" has no client. When a user does log in the "user manager" checks the configuration for that user and starts a process (which becomes the "user manager's" client). Different users can have different configuration, so one user might log in and "fancy GUI" will be started, a different user might log in and "simple GUI" might be started. The administrator might configure things so that if "guest" logs in, an internet browser is started (so that the browser runs as a full-screen application, and "guest" doesn't get a GUI or anything). If the "user manager's" client disconnects (e.g. is terminated for any reason) then the "user manager" returns to the log in prompt.
For the standardised "user interface" protocol itself, for most of the devices (keyboard, mouse, joystick, touchpad, touchscreen, etc) it's easy to guess and there's no need for me to describe it.
For video, the clients create lists of commands that describe 3D scenes (where a scene can include other scenes) and sends the list/s to their server (which can modify the lists of commands if necessary and add it's own list/s of commands, before sending them to its server, etc). The commands in the list use virtual (x, y, z) co-ordinates and a standardised representation for colours (for e.g. "draw cube from (x1, y1, z1) to (x2, y2, z2) using <colour>"), so that the only piece of software that knows/cares which video mode is being used is the video driver/s themselves. A video driver is responsible for all drawing (but relies on a separate "font engine" process to convert UTF-8 strings into alpha-only bitmaps). A good video driver would cache scenes (so they're only redrawn if/when they change) and use hardware acceleration wherever possible to draw the scenes; and would automatically increase/decrease graphics quality to try to maintain a certain refresh rate.
Of course there'd probably also be libraries to handle half the work; so that (for e.g.) a normal application programmer can use functions like "draw cube()" (to add a command to a list of video commands) and "update()" (to send the a list of video commands), and "create_open_file_dialog()" to spawn a widget process.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: GUI Systems - Comparison and Discussion
Yes. I modelled my GUI after PiciGUI. It does define a reasonable basic set of GUI primitives. On top of that, I defined a graphical control-class (a C++ base class for all UI objects), and then panels, text-controls, buttons and a lot of other useful widgets. I do not have a Window-interface, as I don't think this is useful in embedded systems that rarely will need multiple, movable windows. I'm sure it is possible to actually build a windows-interface on top of the existing widgets, but I just haven't seen any need for doing this yet.berkus wrote:PicoGUI, NeWS..
I do not use IPC or messages in my design. There is a server-thread that redraws part of the display on timeouts, but most of the drawing can done by the widgets themselves.
Re: GUI Systems - Comparison and Discussion
How did you know?berkus wrote:Am I right in assuming that this is not backed by any publicly available design documents with more details? If not, I'd like to read them.
No, there are no design documents. The design is in my head only, but I can give you pointers to code & the interface, but I suspect that is not what you want.
To be brief, the part resembling PicoGUI is a device-driver in RDOS (and thus written in 386 assembler). It handles 1, 16, 24 and 32 bit graphical modes and defines a device-independent interface for C/C++ user mode. It has methods for dots, lines, rectangles, elipses, texts and sprites. There is also a blit and masked blit method. The methods work on bitmap-objects, which can either be a physical screen or a memory-based bitmap. The LGOP codes of PicoGUI are also implemented for all methods. Come to think of it, there is also a clip-rectangle function in order to confine drawing to a specific area. All functions are fully reentrant. This interface is defined here for C ++: http://rdos.net/vc/viewvc.cgi/trunk/ker ... iew=markup
The widgets and graphical controls are all in a usermode C++ library, and thus is much easier to read & understand. However, they don't have any separate documentation at this stage. This interface is (mostly) defined here: http://rdos.net/vc/viewvc.cgi/trunk/ker ... ib/widget/