Skip to content

Commit 974d1b7

Browse files
committed
Initial Version
Excised from the MII emulator Signed-off-by: Michel Pollet <[email protected]>
1 parent a205e4d commit 974d1b7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+24786
-0
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.bash_history
2+
build-*
3+
.vscode
4+
compile_commands.json
5+
cachegrind.out.*
6+
callgrind.out.*
7+
.cache
8+
*_scrot.png

Makefile

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Makefile
2+
#
3+
# Copyright (C) 2020 Michel Pollet <[email protected]>
4+
#
5+
# SPDX-License-Identifier: MIT
6+
7+
CC = gcc
8+
9+
LIBMUI =
10+
MUI_SRC := $(wildcard $(LIBMUI)src/*.c)
11+
12+
vpath %.c $(LIBMUI)src $(LIBMUI)mui_shell
13+
14+
# this is just so we compile the tests if not a submodule
15+
IS_SUBMODULE := ${wildcard ../ui_gl/mii_mui.h}
16+
17+
all : static mui_shell
18+
19+
include $(LIBMUI)Makefile.common
20+
21+
ifeq ($(IS_SUBMODULE),)
22+
all : tests
23+
endif
24+
25+
MUI_OBJ := ${patsubst %, $(OBJ)/%, ${notdir ${MUI_SRC:.c=.o}}}
26+
TARGET_LIB := $(LIB)/libmui.a
27+
28+
.PHONY : mui_shell static tests
29+
mui_shell : $(BIN)/mui_shell
30+
static : $(TARGET_LIB)
31+
tests : | $(TARGET_LIB)
32+
$(MAKE) -j -C tests
33+
@echo " ** To launch the demo, run "
34+
@echo " $(BIN)/mui_shell -f $(LIB)/mui_widgets_demo.so"
35+
36+
$(TARGET_LIB) : $(MUI_OBJ) | $(LIB)
37+
@echo " AR $@"
38+
$(Q)$(AR) rcs $@ $^
39+
40+
#
41+
# The shell program is used to test the UI library using plugins
42+
# It is made using XCB and XKB libraries to have a minimal dependency
43+
# on X11. Also, allows partial updates to be tested properly
44+
#
45+
$(OBJ)/mui_shell.o : CPPFLAGS += -DUI_HAS_XCB=1 -DUI_HAS_XKB=1
46+
$(BIN)/mui_shell : LDLIBS += $(shell pkg-config --libs \
47+
xcb xcb-shm xcb-randr \
48+
xkbcommon-x11)
49+
$(BIN)/mui_shell : LDLIBS += -lm -ldl
50+
$(BIN)/mui_shell : $(OBJ)/mui_shell.o $(LIB)/libmui.a
51+
52+
clean :
53+
rm -rf $(O)
54+
55+
# This is for development purpose. This will recompile the project
56+
# everytime a file is modified.
57+
watch :
58+
while true; do \
59+
clear; $(MAKE) -j all tests; \
60+
inotifywait -qre close_write src mui_shell tests/* ../ui_gl; \
61+
done
62+
63+
compile_commands.json: lsp
64+
lsp:
65+
{ $$(which gmake) CC=gcc V=1 --always-make --dry-run all tests ; } | \
66+
sh ../utils/clangd_gen.sh >compile_commands.json
67+
68+
-include $(OBJ)/*.d

Makefile.common

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Makefile
2+
#
3+
# Copyright (C) 2024 Michel Pollet <[email protected]>
4+
#
5+
# SPDX-License-Identifier: MIT
6+
7+
LIBMUI ?= ../
8+
9+
# for bsd_queue.h and incbin.h
10+
#MII ?= $(LIBMUI)..
11+
#CPPFLAGS += -I$(MII)/libmish/src -I$(MII)/contrib
12+
CPPFLAGS += -I$(LIBMUI)contrib
13+
14+
BUILD_DIR ?= $(LIBMUI)
15+
16+
O := $(BUILD_DIR)build-$(shell $(CC) -dumpmachine)
17+
BIN := $(O)/bin
18+
OBJ := $(O)/obj/libmui
19+
LIB := $(O)/lib
20+
21+
CPPFLAGS += -I$(LIBMUI)src
22+
CPPFLAGS += -I$(LIBMUI)mui_shell
23+
CPPFLAGS += ${shell pkg-config --cflags pixman-1}
24+
LDLIBS += ${shell pkg-config --libs pixman-1}
25+
26+
MUI_VERSION := ${shell \
27+
echo $$(git describe --tags --abbrev=0 2>/dev/null || \
28+
echo "(dev)") \
29+
$$(git log -1 --date=short --pretty="%h %cd")}
30+
CPPFLAGS += -DMUI_VERSION="\"$(MUI_VERSION)\""
31+
32+
OPTIMIZE ?= -O0 -g
33+
CFLAGS += --std=gnu99 -Wall -Wextra
34+
CFLAGS += $(OPTIMIZE)
35+
CFLAGS += -Wno-unused-parameter -Wno-unused-function
36+
# PIC is necessary for the shared library/plugin to work
37+
CFLAGS += -fPIC
38+
39+
#CFLAGS += -fsanitize=address
40+
#LDFLAGS += -fsanitize=address
41+
#LDLIBS +=
42+
43+
ifeq ($(V),1)
44+
Q :=
45+
else
46+
Q := @
47+
endif
48+
49+
# use a .temp file, otherwise the mui_shell tries to reload before the file
50+
# is fully written, and it fails.
51+
# the ${filter} are there to make the sure object files are linked before the .a
52+
$(LIB)/%.so : $(OBJ)/%.o $(LIB)/libmui.a | $(O)
53+
@echo " LDSO $@"
54+
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -shared -fPIC -o [email protected] \
55+
${filter %.o, $^} ${filter %.a, $^} $(LDLIBS) && \
56+
57+
58+
$(OBJ)/%.o : %.c | $(OBJ)
59+
@echo " CC" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} "$<"
60+
$(Q)$(CC) -MMD $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
61+
62+
$(BIN)/% : | $(BIN)
63+
@echo " LD $@"
64+
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
65+
66+
$(O) $(OBJ) $(BIN) $(LIB):
67+
@mkdir -p $@

README.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
2+
# What the hell is this?
3+
This is a contender for the World Record for Feature Creep Side Project. It is pretty high in the contender list as it's a bolt on to *another* contender for the World Record for Feature Creep Side Project (the [MII Apple //e emulator](http://github.com/buserror/mii_emu)).
4+
5+
It is a library that duplicate a lot of a Macintosh Classic "Toolbox" APIs. It is not a complete implementation, but it is enough to make a few simple applications, also, all the bits I needed for the MII emulator.
6+
7+
<center>
8+
<img src="doc/widgets.gif" alt="10 seconds demo">
9+
<i>10 seconds demo</i>
10+
</center>
11+
12+
# Why?
13+
Well I wanted a UI library for MII -- something without tons of dependencies, and I didn't want the typical 'game like' style with "Arrow Keys + Return + Escape" sort of menus.
14+
15+
I started with Nuklear immediate mode UI, but it both look super ugly, AND is very limited as soon as you want to do something 'custom', and I didn't see myself hacking into that codebase. One thing I particularly dislike is the 'layout' engine that decide to put stuff where it wants, and it's *never* in the 'right' place, like a hard case of 'computer say so' -- typicaly result into
16+
something like Programmer's Art, just worse. That's why Linux On The Deskop is famous for it's Pixel Perfection polished UIs. *Cough*.
17+
18+
The other things I don't like with the trendy IM UIs is that they promise you that you don't have to keep a separate state around blah blah, however they forget to mention that there IS a state kept for you, based on hash values, and if you are unlucky enough to have a hash clash, you are screwed. I've seen that happen in real life, and it's not pretty to debug.
19+
20+
I miss the days were UIs were /crafted/ not just decided for you bad a bad 'layouting' engine with huge rectangular flat buttons and no sense whatsoever of 'design' or usability.
21+
22+
Also, I realized I had quite a few bits that I could use to make my own library anyway, so I started to do that.
23+
24+
Also, I like making my own toys. There, happy now?
25+
26+
<center>
27+
<img src="doc/control_demo.png" alt="Basic Controls">
28+
<i>Control Demo</i>
29+
</center>
30+
31+
# What can it do?
32+
Well, it has a few of the classic 'managers' that the Macintosh Toolbox had. Or the later GS/OS. However it behaves more like a modern system, it's not 'synchronous' like the old toolbox. Stuff gets redrawn even if you are clicking/dragging etc without having to 'do it yourself'.
33+
34+
It is designed to draw into a 'screen' that is an ARGB buffer. You can then copy that to wherever you fancy.
35+
* In the case of MII, it's an OpenGL texture that gets overlayed;
36+
* In the 'example' folder, the playground demo copies it to an X11 window via a XCB 'shared' pixmap, so works great even via remote X11.
37+
The library is 'smart', like the old OSes, it keeps track of 'invalid' regions, and only redraws what is needed, so theres very very little overdraw.
38+
39+
One small drawback is that the output *has* to be ARGB -- so if you want to render say on a 16 bit framebuffer, you'll have to do the conversion yourself,
40+
luckily, you only have to draw/convert the 'dirty' regions, so it's not too bad.
41+
42+
It could be possible to 'vectorize' the rendering to vertice buffers and stuff, but really, it's not needed, it's fast enough as it is and it would fall back the 'lets redraw everything' behaviour of the IMmediate UI libraries.
43+
44+
<center>
45+
<img src="doc/static_text.png" alt="Basic text boxes">
46+
<i>Some basic text boxes</i>
47+
</center>
48+
49+
50+
# How does it differ from the original?
51+
Well, in terms of looks, it's kinda like I started with MacOS8/9, but removed all the grayscale bits.
52+
Bizarelly, I think System 7 'flat' looks has aged better than the 'grayscale' look of 8/9, so I went with that. Most of the 'visible' difference are really related to the dialog's popup menus, which are a lot more 'OS8' than 'OS7'. The Scrollbar is definitely more GS/OS though, never understood why it took so long for MacOS to adopt that.
53+
54+
In terms of of the API, one massive change is that it is fully asynchronous, and
55+
you *can't* just spinloop and draw things in a window or GrafPort whenever you feel like it, like on the original. Instead, you change the state of the UI, and it will redraw itself when it needs to. This is a lot more like modern UI libraries in that respect.
56+
57+
Many things will look familiar, if like me you were a developer back then,
58+
I tried to use most of the names of things as is; and I even have all the
59+
elements drawn by WDEF, CDEF, LDEF functions, which is fun. Also pretty easy
60+
to customize. by adding your own, like in the old days.
61+
62+
Also, for the API, it's all 'callback' based. No more polling. You don't have
63+
to 'ask' the UI if something happened, it will tell you when it does. This is
64+
also a lot more like modern UI libraries.
65+
66+
It is also a lot simpler than the original in concept; everything is either a *mui_window* (Well, windows, menubars and menus) OR a *mui_control* (Menu titles, menu items, everything in windows, even separator lines).
67+
68+
## Window Manager
69+
It can create windows, and it can draw into them. Has up to 15 'layers', and can do clipping and stuff. Got the typical 'BringToFront' behaviour, and you can drag windows around.
70+
71+
I deliberately limited the number of coordinate systems to 2 -- like the old one; so you get the 'screen coordinates' and the 'window content coordinates'. I was half tempted to create a fully hierarchical system, but realized it was really not neeeded, and would just make things more complicated.
72+
73+
It's a 'smart' window manager, it keeps track of an 'invalid' list of rectangles, and clips to that when redrawing, so it doesn't redraw the whole window every time, yeah, like the original. None of that 'lets redraw absolutely everything every frame' stuff like Immediate Mode UIs.
74+
- It's missing bits like 'zooming' (TODO), and 'resizing' (TODO).
75+
- It doesn't do transparent windows. It is by design, it draws windows 'top down' to optimize clipping -- adding transparency wouldn't be hard, but I'd have to draw the windows 'bottom up' to handle blending, and we'd revert back to drawing a lot of stuff for very little return.
76+
- Also, you can always alpha blend the whole *ui* 'screen' to wherever you want, so it's not like you can't have transparency.
77+
78+
## Menu Manager
79+
Menubar, menus, checkmarks, keyboard shortcuts, all that stuff. Made to looks like System 7/8, or GS/OS. This was the most complicated bit to do, and it's still not perfect -- turns out the original had quite a few features that are not obvious at first glance.
80+
- It has hierarchical menus, they don't behave *perfectly* like the original yet, I'll have to revisit that for a better match.
81+
- It's missing displaying and scrolling super large popups (TODO).
82+
+ It will call your 'action' callback when you click on a menu item, or when you press the key of a keyboard shortcut. Easy, peasy.
83+
+ There is half baked support for sticky menus, but it's disabled for now as it's not quite right yet.
84+
85+
## Control Manager
86+
Buttons, checkboxes, radio buttons, scrollbars (vertical), wrapping textboxes, all that stuff.
87+
- It's missing bits like Edit Field (Work in Progress), and a Slider.
88+
- There IS a prototype version of a text edit control, but it's not quite right yet -- works fine for a one liner etc, but not for a multi line text box. Not far off tho.
89+
## List Manager
90+
More or less hard coded to display filenames so far, but plain lists are actually easier than this so. Handle arrow keys/page up/down, scroll wheel, etc.
91+
- It's missing a way to 'compress' the font and/or use ellipsis abreviations (TODO) when the item text is too long.
92+
+ You CAN use 'typeahead' to find the item you want, like the original.
93+
## Alerts
94+
It has the typical 'Cancel'+'OK' alert.
95+
- Could do with more types of alerts (TODO).
96+
<center>
97+
<img src="doc/alert.png" alt="Basic Alert">
98+
<i>Basic Alert Dialog</i>
99+
</center>
100+
101+
## Standard File
102+
It has the classic 'Open' a file dialog. Haven't needed the other one. yet. This one one of the primary goal of the UI library to start with, so I spent quite a bit of time getting it 'right', quite happy about how it turned out.
103+
- Could do with a 'Save' dialog (TODO).
104+
+ It has an extra popup with the last few used directories.
105+
- You can use arrow keys, page/up down, and you can even typehead to the file you want, like in the old MacOS!
106+
107+
<center>
108+
<img src="doc/standard_get_file.png" alt="Standard Get File">
109+
<i>Standard Get File</i>
110+
</center>
111+
112+
## Resource Manager
113+
Nope! Not there; I'd need some sort of ResEdit and stuff -- and now that is *ONE* Feature Creep Too Far thank you very much.
114+
I have a vague idea of making some sort of MessagePack format for resources, but that's for another day.
115+
116+
# So, what *are* the dependencies?
117+
Well, *external* dependencies are just *libpixman* -- that's it. It's a library that does pixel pushing, it's used everywhere, it's massively optimized, and it has 'regions' which are super useful for clipping -- they aren't *as good* as the regions in QuickDraw, but they are good enough.
118+
119+
Other bits I used to make it are rolled into the source code anyway, so it's not like you need to install them.
120+
* libpixman: as mentioned, you'll need libpixman-dev installed.
121+
* [libcg](https://github.com/xboot/libcg): a small antialiased renderer that looks vaguely like *cairo* but comes in just 2 files, with a nice licence. It's likely far from being the most optimized, but it's largely good enough.
122+
* [stb_truetype.h](https://github.com/nothings/stb): a small library to load truetype fonts, it's used to load the system fonts.
123+
* stb_ttc.h: my own extension to stb_truetype.h, makes up a font/glyph dictionary with hash tables, font textures etc.
124+
125+
That's it, all the other bits I already had 'in stock' -- the 2D geometry bits I made 25+ years ago, they were rolled in [libc3](https://github.com/buserror/simreprap) as well.
126+
127+
# How do I build it?
128+
It's a simple Makefile, so you just need to do a 'make' in the root directory. It will build the library, and the tests/demos/samples.
129+
To build the tests/demos/samples, you'll need:
130+
* xcb xcb-shm xcb-randr xkbcommon-x11 -- this is just to run the 'mui_shell' demo, which is a simple window with a few buttons and stuff.
131+
## Nvidia Driver gotcha
132+
* *Note* that if you use the nvidia binary driver (I do), you will need to add a flag to your config, otherwise the mui_shell won't work.
133+
- Add: 'Option \"AllowSHMPixmaps\" \"1\"' to the "Device" In your /etc/X11/xorg.conf file.
134+
135+
# How do I use it?
136+
Well the best way is to hack around *mui_shell.c* and *mui_widgets_demo.c*. It's a simple window with a few buttons and stuff, and it's a good place to start.
137+
138+
The cool thing about ui_mui_shell is that it loads mui_widgets_demo.so as a *plugin* and auto-reload it if it detects a change. So you can hack around mui_widgets_demo.c, and it will reload it and run it again! You can code a new dialog insanely fast with that, pretty must as fast as you would with a resource editor.
139+
140+
A good trick is to use 'make watch' on the *libmui* directory in a terminal tab, and it will rebuild the library and the mui_shell automatically when you change something, that with the 'auto save' of your editor, and you will have a constantly building/running mui_shell as you hack around.
141+
142+
Have fun!
143+
144+
# FAQ!
145+
* Ok so, is there a dark mode?
146+
- nope
147+
* Fine, but at least it is themable, right?
148+
- nope
149+
* Tranparent Windows and a Cube Effect ?!@?!
150+
- nope
151+
* What, menu aren't sticky? They don't stay up when you release the mouse button? Common throw us a bone here man, it's 2023!
152+
- nope
153+
* But, is it at least using the super trendy tools? cmake? meson? ninja?
154+
- nope
155+
* What about autotools, seriously?
156+
- nope
157+
* Are there any bindings for any of the fancy new languages? Rust? Go? Python?
158+
- nope
159+
* Is it using any of the super trendy framerworks? GTK? QT?
160+
- nope
161+
* What about SDL? It's cross Platform, it is Ugly Everywhere Equally!
162+
- nope
163+
* And Wayland then? Wayland is The Future after all!
164+
- nope
165+
<center><h1>Now, GET OFF MY LAWN!</h1></center>

0 commit comments

Comments
 (0)