diff --git a/.clangd b/.clangd new file mode 100644 index 00000000..c2520c5c --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +Completion: + HeaderInsertion: Never diff --git a/BorMakefile b/BorMakefile index 59551b5f..f8eaced6 100644 --- a/BorMakefile +++ b/BorMakefile @@ -27,10 +27,6 @@ clean: del /f /q src\dialog\messagebox.obj del /f /q src\draw.obj del /f /q src\error.obj - del /f /q src\font\boldfont.obj - del /f /q src\font\boldttf.obj - del /f /q src\font\font.obj - del /f /q src\font\ttf.obj del /f /q src\icon\back.obj del /f /q src\icon\clock.obj del /f /q src\icon\computer.obj @@ -49,7 +45,13 @@ clean: del /f /q src\icon\warning.obj del /f /q src\lowlevel.obj del /f /q src\string.obj - del /f /q src\text.obj + del /f /q src\text\font\boldfont.obj + del /f /q src\text\font\boldttf.obj + del /f /q src\text\font\font.obj + del /f /q src\text\font\ttf.obj + del /f /q src\text\ft2.obj + del /f /q src\text\stbtt.obj + del /f /q src\text\text.obj del /f /q src\unicode.obj del /f /q src\widget\box.obj del /f /q src\widget\button.obj @@ -74,8 +76,8 @@ clean: del /f /q src\Mw.dll del /f /q src\Mw.lib -src\Mw.dll: external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\font\boldfont.obj src\font\boldttf.obj src\font\font.obj src\font\ttf.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj - $(LD) $(LDFLAGS) -e$@ external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\font\boldfont.obj src\font\boldttf.obj src\font\font.obj src\font\ttf.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj -lopengl32.lib -lgdi32.lib -luser32.lib +src\Mw.dll: external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text\font\boldfont.obj src\text\font\boldttf.obj src\text\font\font.obj src\text\font\ttf.obj src\text\ft2.obj src\text\stbtt.obj src\text\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj + $(LD) $(LDFLAGS) -e$@ external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text\font\boldfont.obj src\text\font\boldttf.obj src\text\font\font.obj src\text\font\ttf.obj src\text\ft2.obj src\text\stbtt.obj src\text\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj -lopengl32.lib -lgdi32.lib -luser32.lib implib src\Mw.lib src\Mw.dll .c.obj: diff --git a/CMakeLists.txt b/CMakeLists.txt index 650d6d1e..09a5bd90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ option(MW_INSTALL_HEADERS "Install headers" ON) file( GLOB SOURCES - src/*.c src/cursor/*.c src/icon/*.c src/text.c src/widget/*.c src/math/*.c src/font/*.c src/dialog/*.c src/abstract/*.c external/*.c + src/*.c src/cursor/*.c src/icon/*.c src/widget/*.c src/text/*.c src/text/font/*.c src/dialog/*.c src/abstract/*.c external/*.c ) list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/widget/opengl.c") diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..7921f0c6 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,70 @@ +pipeline { + agent { + label "built-in" + } + stages { + stage("Build document") { + when { + branch "master" + } + steps { + sh("doxygen") + sh("rm -rf /var/www/milsko-doxygen") + sh("mv doxygen/html /var/www/milsko-doxygen") + } + post { + always { + notifyDiscord() + } + } + } + stage("Build") { + parallel { + stage("Build for Linux 64-bit") { + agent { + label "built-in" + } + steps { + sh("./configure --enable-opengl --enable-vulkan --without-vulkan-string-helper") + sh("make clean") + sh("make -j4") + } + } + stage("Build for Windows 32-bit") { + agent { + label "built-in" + } + steps { + sh("./configure --enable-opengl --enable-stb-truetype --disable-freetype2 --cross --target=Windows --host=i686-w64-mingw32") + sh("make clean") + sh("make -j4") + } + } + stage("Build for Windows 64-bit") { + agent { + label "built-in" + } + steps { + sh("./configure --enable-opengl --enable-stb-truetype --disable-freetype2 --cross --target=Windows --host=x86_64-w64-mingw32") + sh("make clean") + sh("make -j4") + } + } + stage("Build for Windows 64-bit (MSVC)") { + agent { + label "2012r2" + } + steps { + bat("nmake -f NTMakefile clean") + bat("nmake -f NTMakefile") + } + } + } + post { + always { + notifyDiscord() + } + } + } + } +} diff --git a/Koakumafile b/Koakumafile deleted file mode 100755 index 92cb41be..00000000 --- a/Koakumafile +++ /dev/null @@ -1,24 +0,0 @@ -# vim: syntax=tcl - -proc run {project_name} { - if { "$project_name" == "MilskoDoxygen" } { - RunCommand "doxygen" - RunCommand "rm -rf /var/www/milsko-doxygen" - RunCommand "mv doxygen/html /var/www/milsko-doxygen" - } else { - foreach target {"Linux" "Win32" "Win64"} { - if { [file exists "Makefile"] == 1 } { - RunCommand "make distclean" - } - if { "$target" == "Linux" } { - RunCommand "./Makefile.pl --enable-opengl --enable-vulkan --without-vulkan-string-helper" - } elseif { "$target" == "Win32" } { - RunCommand "./Makefile.pl --enable-opengl --enable-stb-truetype --disable-freetype2 --cross --target=Windows --host=i686-w64-mingw32" - } elseif { "$target" == "Win64" } { - RunCommand "./Makefile.pl --enable-opengl --enable-stb-truetype --disable-freetype2 --cross --target=Windows --host=x86_64-w64-mingw32" - } - RunCommand "make clean" - RunCommand "make -j4" - } - } -} diff --git a/NTMakefile b/NTMakefile index 265418a7..3177f048 100644 --- a/NTMakefile +++ b/NTMakefile @@ -27,10 +27,6 @@ clean: del /f /q src\dialog\messagebox.obj del /f /q src\draw.obj del /f /q src\error.obj - del /f /q src\font\boldfont.obj - del /f /q src\font\boldttf.obj - del /f /q src\font\font.obj - del /f /q src\font\ttf.obj del /f /q src\icon\back.obj del /f /q src\icon\clock.obj del /f /q src\icon\computer.obj @@ -49,7 +45,13 @@ clean: del /f /q src\icon\warning.obj del /f /q src\lowlevel.obj del /f /q src\string.obj - del /f /q src\text.obj + del /f /q src\text\font\boldfont.obj + del /f /q src\text\font\boldttf.obj + del /f /q src\text\font\font.obj + del /f /q src\text\font\ttf.obj + del /f /q src\text\ft2.obj + del /f /q src\text\stbtt.obj + del /f /q src\text\text.obj del /f /q src\unicode.obj del /f /q src\widget\box.obj del /f /q src\widget\button.obj @@ -74,8 +76,8 @@ clean: del /f /q src\Mw.dll del /f /q src\Mw.lib -src\Mw.dll: external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\font\boldfont.obj src\font\boldttf.obj src\font\font.obj src\font\ttf.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj - $(LD) $(LDFLAGS) /OUT:$@ external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\font\boldfont.obj src\font\boldttf.obj src\font\font.obj src\font\ttf.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj opengl32.lib gdi32.lib user32.lib +src\Mw.dll: external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text\font\boldfont.obj src\text\font\boldttf.obj src\text\font\font.obj src\text\font\ttf.obj src\text\ft2.obj src\text\stbtt.obj src\text\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj + $(LD) $(LDFLAGS) /OUT:$@ external\stb_ds.obj external\stb_image.obj external\stb_truetype.obj src\abstract\directory.obj src\abstract\dynamic.obj src\abstract\time.obj src\backend\gdi.obj src\color.obj src\core.obj src\cursor\arrow.obj src\cursor\cross.obj src\cursor\default.obj src\cursor\hidden.obj src\cursor\text.obj src\default.obj src\dialog\colorpicker.obj src\dialog\directorychooser.obj src\dialog\filechooser.obj src\dialog\messagebox.obj src\draw.obj src\error.obj src\icon\back.obj src\icon\clock.obj src\icon\computer.obj src\icon\directory.obj src\icon\down.obj src\icon\error.obj src\icon\file.obj src\icon\forward.obj src\icon\info.obj src\icon\left.obj src\icon\news.obj src\icon\note.obj src\icon\right.obj src\icon\search.obj src\icon\up.obj src\icon\warning.obj src\lowlevel.obj src\string.obj src\text\font\boldfont.obj src\text\font\boldttf.obj src\text\font\font.obj src\text\font\ttf.obj src\text\ft2.obj src\text\stbtt.obj src\text\text.obj src\unicode.obj src\widget\box.obj src\widget\button.obj src\widget\checkbox.obj src\widget\combobox.obj src\widget\entry.obj src\widget\frame.obj src\widget\image.obj src\widget\label.obj src\widget\listbox.obj src\widget\menu.obj src\widget\numberentry.obj src\widget\opengl.obj src\widget\progressbar.obj src\widget\radiobox.obj src\widget\scrollbar.obj src\widget\separator.obj src\widget\submenu.obj src\widget\treeview.obj src\widget\viewport.obj src\widget\window.obj opengl32.lib gdi32.lib user32.lib .c.obj: diff --git a/README.txt b/README.txt index 32e8630d..dc9d6bf2 100644 --- a/README.txt +++ b/README.txt @@ -6,8 +6,10 @@ distributions and building instructions for Milsko GUI Toolkit. Requirements - Milsko requires the Windows environment with GDI (so anything NT or 9x) or -the Unix-like environment with X11 for runtime. + Milsko requires either + * A Windows environment with GDI (so anything NT or 9x) + * A MacOS environment with Cocoa (10.4 or above supported) + * A Unix-like environment with X11 for runtime. To build Milsko for Windows, you must have one of following compilers: * Visual C++ 6.0 or newer @@ -15,7 +17,7 @@ the Unix-like environment with X11 for runtime. * Open Watcom 2.0 or newer * MinGW-w64 - and for Unix-like: + and for Unix-like and MacOS: * GNU C Compiler * Clang @@ -61,8 +63,8 @@ D. MinGW-w64/GCC/Clang 1) Determine if you need Vulkan and/or OpenGL. -2) Run `./Makefile.pl'. - For help, run `./Makefile.pl --help'. +2) Run `./configure'. + For help, run `./configure --help'. 3) Run `make'. diff --git a/WatMakefile b/WatMakefile index dc7b1211..5177e050 100644 --- a/WatMakefile +++ b/WatMakefile @@ -26,10 +26,6 @@ clean: .SYMBOLIC %erase src/dialog/messagebox.obj %erase src/draw.obj %erase src/error.obj - %erase src/font/boldfont.obj - %erase src/font/boldttf.obj - %erase src/font/font.obj - %erase src/font/ttf.obj %erase src/icon/back.obj %erase src/icon/clock.obj %erase src/icon/computer.obj @@ -48,7 +44,13 @@ clean: .SYMBOLIC %erase src/icon/warning.obj %erase src/lowlevel.obj %erase src/string.obj - %erase src/text.obj + %erase src/text/font/boldfont.obj + %erase src/text/font/boldttf.obj + %erase src/text/font/font.obj + %erase src/text/font/ttf.obj + %erase src/text/ft2.obj + %erase src/text/stbtt.obj + %erase src/text/text.obj %erase src/unicode.obj %erase src/widget/box.obj %erase src/widget/button.obj @@ -73,8 +75,8 @@ clean: .SYMBOLIC %erase src/Mw.dll %erase src/Mw.lib -src/Mw.dll: external/stb_ds.obj external/stb_image.obj external/stb_truetype.obj src/abstract/directory.obj src/abstract/dynamic.obj src/abstract/time.obj src/backend/gdi.obj src/color.obj src/core.obj src/cursor/arrow.obj src/cursor/cross.obj src/cursor/default.obj src/cursor/hidden.obj src/cursor/text.obj src/default.obj src/dialog/colorpicker.obj src/dialog/directorychooser.obj src/dialog/filechooser.obj src/dialog/messagebox.obj src/draw.obj src/error.obj src/font/boldfont.obj src/font/boldttf.obj src/font/font.obj src/font/ttf.obj src/icon/back.obj src/icon/clock.obj src/icon/computer.obj src/icon/directory.obj src/icon/down.obj src/icon/error.obj src/icon/file.obj src/icon/forward.obj src/icon/info.obj src/icon/left.obj src/icon/news.obj src/icon/note.obj src/icon/right.obj src/icon/search.obj src/icon/up.obj src/icon/warning.obj src/lowlevel.obj src/string.obj src/text.obj src/unicode.obj src/widget/box.obj src/widget/button.obj src/widget/checkbox.obj src/widget/combobox.obj src/widget/entry.obj src/widget/frame.obj src/widget/image.obj src/widget/label.obj src/widget/listbox.obj src/widget/menu.obj src/widget/numberentry.obj src/widget/opengl.obj src/widget/progressbar.obj src/widget/radiobox.obj src/widget/scrollbar.obj src/widget/separator.obj src/widget/submenu.obj src/widget/treeview.obj src/widget/viewport.obj src/widget/window.obj - $(LD) $(LDFLAGS) option implib=src/Mw.lib name $@ file external/stb_ds.obj file external/stb_image.obj file external/stb_truetype.obj file src/abstract/directory.obj file src/abstract/dynamic.obj file src/abstract/time.obj file src/backend/gdi.obj file src/color.obj file src/core.obj file src/cursor/arrow.obj file src/cursor/cross.obj file src/cursor/default.obj file src/cursor/hidden.obj file src/cursor/text.obj file src/default.obj file src/dialog/colorpicker.obj file src/dialog/directorychooser.obj file src/dialog/filechooser.obj file src/dialog/messagebox.obj file src/draw.obj file src/error.obj file src/font/boldfont.obj file src/font/boldttf.obj file src/font/font.obj file src/font/ttf.obj file src/icon/back.obj file src/icon/clock.obj file src/icon/computer.obj file src/icon/directory.obj file src/icon/down.obj file src/icon/error.obj file src/icon/file.obj file src/icon/forward.obj file src/icon/info.obj file src/icon/left.obj file src/icon/news.obj file src/icon/note.obj file src/icon/right.obj file src/icon/search.obj file src/icon/up.obj file src/icon/warning.obj file src/lowlevel.obj file src/string.obj file src/text.obj file src/unicode.obj file src/widget/box.obj file src/widget/button.obj file src/widget/checkbox.obj file src/widget/combobox.obj file src/widget/entry.obj file src/widget/frame.obj file src/widget/image.obj file src/widget/label.obj file src/widget/listbox.obj file src/widget/menu.obj file src/widget/numberentry.obj file src/widget/opengl.obj file src/widget/progressbar.obj file src/widget/radiobox.obj file src/widget/scrollbar.obj file src/widget/separator.obj file src/widget/submenu.obj file src/widget/treeview.obj file src/widget/viewport.obj file src/widget/window.obj library clib3r.lib library opengl32.lib library gdi32.lib library user32.lib +src/Mw.dll: external/stb_ds.obj external/stb_image.obj external/stb_truetype.obj src/abstract/directory.obj src/abstract/dynamic.obj src/abstract/time.obj src/backend/gdi.obj src/color.obj src/core.obj src/cursor/arrow.obj src/cursor/cross.obj src/cursor/default.obj src/cursor/hidden.obj src/cursor/text.obj src/default.obj src/dialog/colorpicker.obj src/dialog/directorychooser.obj src/dialog/filechooser.obj src/dialog/messagebox.obj src/draw.obj src/error.obj src/icon/back.obj src/icon/clock.obj src/icon/computer.obj src/icon/directory.obj src/icon/down.obj src/icon/error.obj src/icon/file.obj src/icon/forward.obj src/icon/info.obj src/icon/left.obj src/icon/news.obj src/icon/note.obj src/icon/right.obj src/icon/search.obj src/icon/up.obj src/icon/warning.obj src/lowlevel.obj src/string.obj src/text/font/boldfont.obj src/text/font/boldttf.obj src/text/font/font.obj src/text/font/ttf.obj src/text/ft2.obj src/text/stbtt.obj src/text/text.obj src/unicode.obj src/widget/box.obj src/widget/button.obj src/widget/checkbox.obj src/widget/combobox.obj src/widget/entry.obj src/widget/frame.obj src/widget/image.obj src/widget/label.obj src/widget/listbox.obj src/widget/menu.obj src/widget/numberentry.obj src/widget/opengl.obj src/widget/progressbar.obj src/widget/radiobox.obj src/widget/scrollbar.obj src/widget/separator.obj src/widget/submenu.obj src/widget/treeview.obj src/widget/viewport.obj src/widget/window.obj + $(LD) $(LDFLAGS) option implib=src/Mw.lib name $@ file external/stb_ds.obj file external/stb_image.obj file external/stb_truetype.obj file src/abstract/directory.obj file src/abstract/dynamic.obj file src/abstract/time.obj file src/backend/gdi.obj file src/color.obj file src/core.obj file src/cursor/arrow.obj file src/cursor/cross.obj file src/cursor/default.obj file src/cursor/hidden.obj file src/cursor/text.obj file src/default.obj file src/dialog/colorpicker.obj file src/dialog/directorychooser.obj file src/dialog/filechooser.obj file src/dialog/messagebox.obj file src/draw.obj file src/error.obj file src/icon/back.obj file src/icon/clock.obj file src/icon/computer.obj file src/icon/directory.obj file src/icon/down.obj file src/icon/error.obj file src/icon/file.obj file src/icon/forward.obj file src/icon/info.obj file src/icon/left.obj file src/icon/news.obj file src/icon/note.obj file src/icon/right.obj file src/icon/search.obj file src/icon/up.obj file src/icon/warning.obj file src/lowlevel.obj file src/string.obj file src/text/font/boldfont.obj file src/text/font/boldttf.obj file src/text/font/font.obj file src/text/font/ttf.obj file src/text/ft2.obj file src/text/stbtt.obj file src/text/text.obj file src/unicode.obj file src/widget/box.obj file src/widget/button.obj file src/widget/checkbox.obj file src/widget/combobox.obj file src/widget/entry.obj file src/widget/frame.obj file src/widget/image.obj file src/widget/label.obj file src/widget/listbox.obj file src/widget/menu.obj file src/widget/numberentry.obj file src/widget/opengl.obj file src/widget/progressbar.obj file src/widget/radiobox.obj file src/widget/scrollbar.obj file src/widget/separator.obj file src/widget/submenu.obj file src/widget/treeview.obj file src/widget/viewport.obj file src/widget/window.obj library clib3r.lib library opengl32.lib library gdi32.lib library user32.lib @@ -120,14 +122,6 @@ src/draw.obj: src/draw.c $(CC) $(CFLAGS) -fo=$@ $< src/error.obj: src/error.c $(CC) $(CFLAGS) -fo=$@ $< -src/font/boldfont.obj: src/font/boldfont.c - $(CC) $(CFLAGS) -fo=$@ $< -src/font/boldttf.obj: src/font/boldttf.c - $(CC) $(CFLAGS) -fo=$@ $< -src/font/font.obj: src/font/font.c - $(CC) $(CFLAGS) -fo=$@ $< -src/font/ttf.obj: src/font/ttf.c - $(CC) $(CFLAGS) -fo=$@ $< src/icon/back.obj: src/icon/back.c $(CC) $(CFLAGS) -fo=$@ $< src/icon/clock.obj: src/icon/clock.c @@ -164,7 +158,19 @@ src/lowlevel.obj: src/lowlevel.c $(CC) $(CFLAGS) -fo=$@ $< src/string.obj: src/string.c $(CC) $(CFLAGS) -fo=$@ $< -src/text.obj: src/text.c +src/text/font/boldfont.obj: src/text/font/boldfont.c + $(CC) $(CFLAGS) -fo=$@ $< +src/text/font/boldttf.obj: src/text/font/boldttf.c + $(CC) $(CFLAGS) -fo=$@ $< +src/text/font/font.obj: src/text/font/font.c + $(CC) $(CFLAGS) -fo=$@ $< +src/text/font/ttf.obj: src/text/font/ttf.c + $(CC) $(CFLAGS) -fo=$@ $< +src/text/ft2.obj: src/text/ft2.c + $(CC) $(CFLAGS) -fo=$@ $< +src/text/stbtt.obj: src/text/stbtt.c + $(CC) $(CFLAGS) -fo=$@ $< +src/text/text.obj: src/text/text.c $(CC) $(CFLAGS) -fo=$@ $< src/unicode.obj: src/unicode.c $(CC) $(CFLAGS) -fo=$@ $< diff --git a/Makefile.pl b/configure similarity index 98% rename from Makefile.pl rename to configure index 519781ec..5ffbe544 100755 --- a/Makefile.pl +++ b/configure @@ -187,7 +187,7 @@ print(OUT " clang-format --verbose -i `find src include examples -name \"*.c\" -or -name \"*.h\" -or -name \"*.m\"`\n" ); print(OUT -" perltidy -b -bext=\"/\" --paren-tightness=2 `find tools pl Makefile.pl -name \"*.pl\"`\n" +" perltidy -b -bext=\"/\" --paren-tightness=2 `find tools pl configure -name \"*.pl\"`\n" ); print(OUT "\n"); print(OUT "lib:"); diff --git a/examples/basic/clipboard.c b/examples/basic/clipboard.c index c8882d35..57d57925 100644 --- a/examples/basic/clipboard.c +++ b/examples/basic/clipboard.c @@ -3,7 +3,7 @@ MwWidget window, instructions, text; void resize(MwWidget handle, void* user_data, void* call_data) { - unsigned int w, h, mh; + unsigned int w, h; (void)user_data; (void)call_data; @@ -12,13 +12,13 @@ void resize(MwWidget handle, void* user_data, void* call_data) { h = MwGetInteger(handle, MwNheight); MwVaApply(instructions, - MwNy, 50 + mh, + MwNy, 50, MwNwidth, w - 50 * 2, MwNheight, h - 125 - 50 * 3, NULL); MwVaApply(text, - MwNy, 200 + mh, + MwNy, 200, MwNwidth, w - 50 * 2, MwNheight, h - 125 - 50 * 3, NULL); @@ -29,8 +29,9 @@ void clipboard(MwWidget handle, void* user_data, void* call_data) { (void)handle; (void)user_data; + printf("got: %s\n", clipboard); if(clipboard != NULL) { - MwVaApply(text, MwNtext, clipboard); + MwVaApply(text, MwNtext, clipboard, NULL); MwForceRender(text); } MwForceRender(window); diff --git a/examples/basic/example.c b/examples/basic/example.c index 614b4e37..9cc89db4 100644 --- a/examples/basic/example.c +++ b/examples/basic/example.c @@ -17,7 +17,7 @@ void handler_dark(MwWidget handle, void* user_data, void* call_data) { (void)call_data; toggle = toggle ? 0 : 1; - MwToggleDarkTheme(window, toggle); + MwSetDarkTheme(window, toggle); } void resize(MwWidget handle, void* user_data, void* call_data) { diff --git a/examples/gldemos/cube.c b/examples/gldemos/cube.c index c1ba9917..2d1954c5 100644 --- a/examples/gldemos/cube.c +++ b/examples/gldemos/cube.c @@ -1,78 +1,89 @@ #define TITLE "cube" #include "glutlayer.c" +#if defined(__APPLE__) +#include +#include +#else +#include #include +#endif static double deg = 0; static void draw(void) { - int i; - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + int i; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glPushMatrix(); - glRotatef(deg, 1, 0, 0); - glRotatef(deg, 0, 1, 0); - glRotatef(deg, 0, 0, 1); + glPushMatrix(); + glRotatef(deg, 1, 0, 0); + glRotatef(deg, 0, 1, 0); + glRotatef(deg, 0, 0, 1); - for(i = 0; i < 6; i++) { - if(i == 0) glColor3f(1, 0, 0); - if(i == 1) glColor3f(0, 1, 0); - if(i == 2) glColor3f(1, 1, 0); - if(i == 3) glColor3f(0, 0, 1); - if(i == 4) glColor3f(1, 0, 1); - if(i == 5) glColor3f(0, 1, 1); + for (i = 0; i < 6; i++) { + if (i == 0) + glColor3f(1, 0, 0); + if (i == 1) + glColor3f(0, 1, 0); + if (i == 2) + glColor3f(1, 1, 0); + if (i == 3) + glColor3f(0, 0, 1); + if (i == 4) + glColor3f(1, 0, 1); + if (i == 5) + glColor3f(0, 1, 1); - glBegin(GL_QUADS); - glNormal3f(0, 1, 0); - glVertex3f(-1, 1, -1); - glVertex3f(-1, 1, 1); - glVertex3f(1, 1, 1); - glVertex3f(1, 1, -1); - glEnd(); + glBegin(GL_QUADS); + glNormal3f(0, 1, 0); + glVertex3f(-1, 1, -1); + glVertex3f(-1, 1, 1); + glVertex3f(1, 1, 1); + glVertex3f(1, 1, -1); + glEnd(); - if(i < 3) glRotatef(90, 0, 0, 1); - if(i == 3) glRotatef(90, 1, 0, 0); - if(i == 4) glRotatef(180, 0, 0, 1); - } - glPopMatrix(); + if (i < 3) + glRotatef(90, 0, 0, 1); + if (i == 3) + glRotatef(90, 1, 0, 0); + if (i == 4) + glRotatef(180, 0, 0, 1); + } + glPopMatrix(); } -static void idle(void) { - deg += 1.0; -} +static void idle(void) { deg += 1.0; } static void reshape(int width, int height) { - GLfloat lpos[4]; + GLfloat lpos[4]; - lpos[0] = 1; - lpos[1] = 1; - lpos[2] = 1; - lpos[3] = 0; + lpos[0] = 1; + lpos[1] = 1; + lpos[2] = 1; + lpos[3] = 0; - glViewport(0, 0, width, height); + glViewport(0, 0, width, height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(60, (double)width / height, 1, 100); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60, (double)width / height, 1, 100); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt(2, 2, 2, 0, 0, 0, 0, 1, 0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt(2, 2, 2, 0, 0, 0, 0, 1, 0); - glLightfv(GL_LIGHT0, GL_POSITION, lpos); + glLightfv(GL_LIGHT0, GL_POSITION, lpos); } static void init(void) { - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_COLOR_MATERIAL); - glEnable(GL_CULL_FACE); - glEnable(GL_NORMALIZE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_COLOR_MATERIAL); + glEnable(GL_CULL_FACE); + glEnable(GL_NORMALIZE); - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); } -static void key(int k) { - (void)k; -} +static void key(int k) { (void)k; } diff --git a/examples/vkdemos/vulkan.c b/examples/vkdemos/vulkan.c index 0e1842d0..29547ce5 100644 --- a/examples/vkdemos/vulkan.c +++ b/examples/vkdemos/vulkan.c @@ -27,6 +27,8 @@ MwWidget window, vulkan; int ow = 300; int oh = 250; +double timer = 0; + PFN_vkGetInstanceProcAddr _vkGetInstanceProcAddr; VkInstance instance; VkDevice device; @@ -75,9 +77,10 @@ void tick(MwWidget handle, void* user_data, void* call_data) { VkSubmitInfo submitInfo = {}; VkPresentInfoKHR presentInfo = {}; - VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + VkClearValue clearColor = {{{sin(timer) / 10., cos(timer) / 10., atan(timer) / 10., 1.0f}}}; uint32_t vertexCount = 3; uint32_t instanceCount = 1; + timer += (float)(rand() % 1000) / 1000.; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT}; @@ -289,8 +292,8 @@ void vulkan_setup(MwWidget handle) { swapchainCreateInfo.imageExtent = (VkExtent2D){.width = ow, .height = oh}, swapchainCreateInfo.imageArrayLayers = 1, swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT; + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT; // th is how we specify no transformation. swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; diff --git a/include/Mw/Core.h b/include/Mw/Core.h index cd7cabd1..aba19e11 100644 --- a/include/Mw/Core.h +++ b/include/Mw/Core.h @@ -296,7 +296,7 @@ MWDECL void MwHideCursor(MwWidget handle); * @param handle Widget * @param toggle Toggle */ -MWDECL void MwToggleDarkTheme(MwWidget handle, int toggle); +MWDECL void MwSetDarkTheme(MwWidget handle, int toggle); /*! * @brief Gets the parent widget diff --git a/include/Mw/LowLevel.h b/include/Mw/LowLevel.h index 6bea6cd3..9766d052 100644 --- a/include/Mw/LowLevel.h +++ b/include/Mw/LowLevel.h @@ -18,10 +18,12 @@ typedef struct _MwLLCommonPixmap* MwLLCommonPixmap; typedef union _MwLL* MwLL; typedef union _MwLLColor* MwLLColor; typedef union _MwLLPixmap* MwLLPixmap; +typedef struct _MwFLFont* MwFLFont; #else typedef void* MwLL; typedef void* MwLLColor; typedef void* MwLLPixmap; +typedef void* MwFLFont; #endif enum MwLLBackends { @@ -163,6 +165,15 @@ struct _MwLLHandler { void (*focus_in)(MwLL handle, void* data); void (*focus_out)(MwLL handle, void* data); void (*clipboard)(MwLL handle, void* data); + void (*dark_theme)(MwLL handle, void* data); +}; + +struct _MwLLTextDispatchTable { + int (*drawText)(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color); + int (*textWidth)(MwWidget handle, const char* text); + int (*textHeight)(MwWidget handle, int count); + void* (*fontLoad)(unsigned char* data, unsigned int size); + void (*fontFree)(void* handle); }; #ifdef __cplusplus @@ -224,6 +235,22 @@ MWDECL void (*MwLLGetClipboard)(MwLL handle); MWDECL void (*MwLLGetCursorCoord)(MwLL handle, MwPoint* point); MWDECL void (*MwLLGetScreenSize)(MwLL handle, MwRect* rect); +/* font renderer */ +MWDECL void MwFLSetup(void); + +#ifdef USE_FREETYPE2 +MWDECL int MWFL_FT2Setup(void); +#endif +#ifdef USE_STB_TRUETYPE +MWDECL int MwFL_STBTTSetup(void); +#endif + +MWDECL int (*MwFLDrawText)(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color); +MWDECL int (*MwFLTextWidth)(MwWidget handle, const char* text); +MWDECL int (*MwFLTextHeight)(MwWidget handle, int count); +MWDECL void* (*MwFLFontLoad)(unsigned char* data, unsigned int size); +MWDECL void (*MwFLFontFree)(void* handle); + #ifdef __cplusplus } #endif diff --git a/include/Mw/LowLevel/Cocoa.h b/include/Mw/LowLevel/Cocoa.h index 2d1d9bde..19e0bc3c 100644 --- a/include/Mw/LowLevel/Cocoa.h +++ b/include/Mw/LowLevel/Cocoa.h @@ -11,10 +11,9 @@ #include #ifdef __OBJC__ - -#import #import #import +#import #ifdef __APPLE__ #import @@ -22,98 +21,170 @@ #import #endif -@interface MilskoCocoaPixmap : NSObject { - int width; - int height; - NSData* data; - NSImage* image; +// Note: implements NSApplicationDelegate +@interface MilskoCocoaApplicationDelegate : NSObject { + NSApplication *appl; +} +- (MilskoCocoaApplicationDelegate *)initWithAppl:(NSApplication *)appl; +@end + +@interface MilskoCocoaWindow : NSWindow { +} +@end + +// Note: implements NSWindowDelegate +@interface MilskoCocoaWindowDelegate : NSObject { + MilskoCocoaWindow *w; +} +- (MilskoCocoaWindowDelegate *)initWithWin:(MilskoCocoaWindow *)win; +@end + +/* + So we want to associate each NSWindow with its corresponding MwLL handle. The + conventional way of doing this is through "associative pointers", however + this is a feature that Apple added in 10.6 (marketted back then as "Objective + C 2" iirc). For 10.4 compatibility, we have to do a bit of an ugly hack that + might seem like horrifically undefined behavior, but I've tested this on + both 10.4 and modern Mac OS and as long as we're careful there's no + problems: we override NSView, use NSView's "frame" property to store the + pointer, and then override functions appropriately so that this frame is never + actually used. This can then be attached to an NSWindow with addSubview and + retrieved appropriately. + */ +@interface MilskoFakePointer : NSView { + void *ptr; } -+ (MilskoCocoaPixmap*)newWithWidth:(int)width height:(int)height; -- (void)updateWithData:(void*)data; +- (void)setPointer:(void *)ptr; +- (void *)pointer; + +@end + +@interface MilskoCocoaPixmap : NSObject { + MwBool valid; + int width; + int height; + unsigned char *buf; + NSImage *image; + NSBitmapImageRep *rep; +} + ++ (MilskoCocoaPixmap *)newWithWidth:(int)width height:(int)height; +- (void)updateWithData:(unsigned char *)data; - (void)destroy; -/* using @property to create instance variables fucks up 10.4 gcc for some reason? */ -- (NSImage*)image; +/* using @property to create instance variables fucks up 10.4 gcc for some + * reason? */ +- (NSImage *)image; @end @interface MilskoCocoaView : NSView { - CGContextRef cg; - CGColorSpaceRef space; - CGDataProviderRef provider; - MwU32* buf; - float width; - float height; + NSBitmapImageRep *rep; + NSGraphicsContext *context; + MwBool valid; + CGColorSpaceRef space; + CGDataProviderRef provider; + float width; + float height; } -- (CGContextRef)context; +- (void)initRepAndContextWithWidth:(float)w Height:(float)h; +- (NSGraphicsContext *)context; +- (void)destroy; +- (NSBitmapImageRep *)getRep; + @end @interface MilskoCocoa : NSObject { - NSApplication* application; - NSWindow* window; - NSRect rect; - MilskoCocoaView* view; + NSApplication *application; + MwBool _forceRender; + MilskoCocoaWindow *window; + NSRect rect; + MilskoCocoaView *view; + MwLL parent; + MilskoFakePointer *handle; + unsigned int strHash; + NSEvent *lastEvent; + + MilskoCocoaPixmap *cursorPixmap; + NSCursor *cursor; + NSModalSession modalSession; + + MwBool pointerLocked; + MwBool mouseMoved; + } -+ (MilskoCocoa*)newWithParent:(MwLL)parent - x:(int)x - y:(int)y - width:(int)width - height:(int)height; -- (void)polygonWithPoints:(MwPoint*)points - points_count:(int)points_count - color:(MwLLColor)color; -- (void)lineWithPoints:(MwPoint*)points color:(MwLLColor)color; -- (void)getX:(int*)x Y:(int*)y W:(unsigned int*)w H:(unsigned int*)h; ++ (MilskoCocoa *)newWithParent:(MwLL)parent + x:(int)x + y:(int)y + width:(int)width + height:(int)height + handle:(MwLL)handle; +- (void)polygonWithPoints:(MwPoint *)points + points_count:(int)points_count + color:(MwLLColor)color; +- (void)lineWithPoints:(MwPoint *)points color:(MwLLColor)color; +- (void)getX:(int *)x Y:(int *)y W:(unsigned int *)w H:(unsigned int *)h; - (void)setX:(int)x Y:(int)y; - (void)setW:(int)w H:(int)h; - (int)pending; +- (void)eventProcess:(NSEvent *)ev; +- (void)handleKeyEvent:(NSEvent *)ev ll:(MwLL)ll; +- (void)handleMouseEvent:(NSEvent *)ev ll:(MwLL)ll; - (void)getNextEvent; -- (void)setTitle:(const char*)title; -- (void)drawPixmap:(MwLLPixmap)pixmap rect:(MwRect*)rect; +- (void)setTitle:(const char *)title; +- (void)drawPixmap:(MwLLPixmap)pixmap rect:(MwRect *)rect; - (void)setIcon:(MwLLPixmap)pixmap; - (void)forceRender; -- (void)setCursor:(MwCursor*)image mask:(MwCursor*)mask; -- (void)detachWithPoint:(MwPoint*)point; +- (void)setCursor:(MwCursor *)image mask:(MwCursor *)mask; +- (void)detachWithPoint:(MwPoint *)point; - (void)show:(int)show; - (void)makePopupWithParent:(MwLL)parent; - (void)setSizeHintsWithMinX:(int)minx - MinY:(int)miny - MaxX:(int)maxx - MaxY:(int)maxy; + MinY:(int)miny + MaxX:(int)maxx + MaxY:(int)maxy; - (void)makeBorderless:(int)toggle; - (void)focus; - (void)grabPointer:(int)toggle; -- (void)setClipboard:(const char*)text; +- (void)setClipboard:(const char *)text; - (void)makeToolWindow; -- (void)getCursorCoord:(MwPoint*)point; -- (void)getScreenSize:(MwRect*)rect; +- (void)getCursorCoord:(MwPoint *)point; +- (void)getScreenSize:(MwRect *)rect; - (void)destroy; +- (void)sendClipboardEvent; + +- (MwLL)getParent; +- (NSView *)getView; +- (MilskoCocoaWindow *)getWindow; + +- (MilskoFakePointer *)getHandle; ++ (void)eventCanceller:(MilskoCocoa *)this; @end #define OBJC(x) x #else -#define OBJC(x) void* +#define OBJC(x) void * #endif MWDECL int MwLLCocoaCallInit(void); struct _MwLLCocoa { - struct _MwLLCommon common; - OBJC(MilskoCocoa*) - real; + struct _MwLLCommon common; + OBJC(MilskoCocoa *) + real; }; struct _MwLLCocoaColor { - struct _MwLLCommonColor common; + struct _MwLLCommonColor common; }; struct _MwLLCocoaPixmap { - struct _MwLLCommonPixmap common; - OBJC(MilskoCocoaPixmap*) - real; + struct _MwLLCommonPixmap common; + OBJC(MilskoCocoaPixmap *) + real; }; #endif diff --git a/include/Mw/LowLevel/GDI.h b/include/Mw/LowLevel/GDI.h index 1bf997c2..c695cbd1 100644 --- a/include/Mw/LowLevel/GDI.h +++ b/include/Mw/LowLevel/GDI.h @@ -22,6 +22,7 @@ struct _MwLLGDI { int grabbed; int force_render; int get_clipboard; + int get_darktheme; }; struct _MwLLGDIColor { diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index 0375eceb..00556dad 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -24,12 +24,21 @@ MWDECL int MwLLWaylandCallInit(void); #ifndef WL_PROTOCOLS_DEFINED #define WL_PROTOCOLS_DEFINED #include "Wayland/xdg-shell-client-protocol.h" +#include "Wayland/viewporter-client-protocol.h" #include "Wayland/xdg-decoration-client-protocol.h" #include "Wayland/cursor-shape-client-protocol.h" #include "Wayland/primary-selection-client-protocol.h" #include "Wayland/xdg-toplevel-icon-client-protocol.h" #endif +#define CSD_BORDER_FRAME_LEFT 5 +#define CSD_BORDER_FRAME_RIGHT 5 +#define CSD_BORDER_FRAME_TOP 24 +#define CSD_BORDER_FRAME_BOTTOM 5 + +#define CSD_LEFT_OFFSET(handle) handle->wayland.has_decorations ? 0 : CSD_BORDER_FRAME_LEFT +#define CSD_TOP_OFFSET(handle) handle->wayland.has_decorations ? 0 : CSD_BORDER_FRAME_TOP + struct _MwLLWayland; typedef struct wayland_protocol { @@ -58,6 +67,9 @@ struct _MwLLWaylandTopLevel { MwBool compositor_created; MwBool xdg_wm_base_created; MwBool xdg_surface_created; + + struct wl_subsurface* ssurface; + struct wl_subcompositor* scompositor; }; struct _MwLLWaylandSublevel { @@ -129,6 +141,9 @@ struct _MwLLWayland { enum _MwLLWaylandType type; enum _MwLLWaylandType type_to_be; + MwBool detatching; + MwPoint detach_point; + /* Map of Wayland interfaces to their relevant setup functions. */ struct { const char* key; @@ -143,13 +158,19 @@ struct _MwLLWayland { MwBool always_render; + MwBool has_decorations; + char title[255]; + struct wl_display* display; struct wl_registry* registry; struct wl_compositor* compositor; struct wl_registry_listener registry_listener; + struct wl_region* o_region; struct wl_region* region; struct wl_output* output; + struct wp_viewport* vp; + /* clipboard related stuff. * Note that unlike most interfaces, we don't keep zwp_primary_selection stuff in a wayland_protocol_t because we use wl_data_device as a fallback and want to have it share memory space.*/ @@ -167,8 +188,10 @@ struct _MwLLWayland { uint32_t clipboard_serial; wl_clipboard_device_context_t** clipboard_devices; - struct wl_pointer* pointer; - MwU32 pointer_serial; + struct wl_pointer* pointer; + MwU32 pointer_serial; + struct wl_seat* pointer_seat; + struct wl_keyboard* keyboard; MwU32 keyboard_serial; @@ -178,7 +201,11 @@ struct _MwLLWayland { MwBool configured; /* Whether or not xdg_toplevel_configure has run once */ - MwU32 x, y, ww, wh; /* Window position */ + MwBool held_down; + + MwI32 x, y; + MwI32 ox, oy; + MwU32 ww, wh; /* Window position */ MwPoint cur_mouse_pos; /* Currently known mouse position */ MwU32 mw, mh; /* Monitor width and height as advertised by wl_output.mode */ @@ -186,8 +213,10 @@ struct _MwLLWayland { MwLL parent; MwBool force_render; + MwBool did_event_loop_early; struct _MwLLWaylandShmBuffer framebuffer; + struct _MwLLWaylandShmBuffer backbuffer; struct _MwLLWaylandShmBuffer cursor; struct _MwLLWaylandShmBuffer* icon; @@ -199,10 +228,16 @@ struct _MwLLWayland { uint32_t last_time; - MwLL currentlyHeldWidget; + MwBool moving; - cairo_surface_t* cs; - cairo_t* cairo; + struct wl_surface* curSurface; + + cairo_surface_t* front_cs; + cairo_surface_t* back_cs; + cairo_t* front_cairo; + cairo_t* back_cairo; + /* The cairo to actually use for draw operations. Typically is front_cairo, but MwLLBeginDraw can change this to the back_cairo so it can be used to draw window decorations. */ + cairo_t* selected_cairo; }; struct _MwLLWaylandColor { diff --git a/include/Mw/StringDefs.h b/include/Mw/StringDefs.h index 5d689659..db597889 100644 --- a/include/Mw/StringDefs.h +++ b/include/Mw/StringDefs.h @@ -72,5 +72,6 @@ #define MwNcolorChosenHandler "CcolorChosen" /* MwRGB* */ #define MwNdrawHandler "Cdraw" /* NULL */ #define MwNclipboardHandler "Cclipboard" /* char* */ +#define MwNdarkThemeHandler "CdarkTheme" /* int* */ #endif diff --git a/include/Mw/Widget/OpenGL.h b/include/Mw/Widget/OpenGL.h index 26f44abf..c3fab76e 100644 --- a/include/Mw/Widget/OpenGL.h +++ b/include/Mw/Widget/OpenGL.h @@ -5,17 +5,24 @@ #ifndef __MW_WIDGET_OPENGL_H__ #define __MW_WIDGET_OPENGL_H__ +#include #include #include -#include #if !defined(MW_OPENGL_NO_INCLUDE) && !defined(__gl_h_) #ifdef _WIN32 #include +#elif defined(__APPLE__) #else #include #endif +#if defined(__APPLE__) +#include +#include +#else #include +#include +#endif #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY @@ -36,7 +43,7 @@ MWDECL MwClass MwOpenGLClass; * @param handle Widget */ MwInline void MwOpenGLMakeCurrent(MwWidget handle) { - MwVaWidgetExecute(handle, "mwOpenGLMakeCurrent", NULL); + MwVaWidgetExecute(handle, "mwOpenGLMakeCurrent", NULL); } /*! @@ -45,10 +52,10 @@ MwInline void MwOpenGLMakeCurrent(MwWidget handle) { * @param name Name * @return Procedure */ -MwInline void* MwOpenGLGetProcAddress(MwWidget handle, const char* name) { - void* out; - MwVaWidgetExecute(handle, "mwOpenGLGetProcAddress", &out, name); - return out; +MwInline void *MwOpenGLGetProcAddress(MwWidget handle, const char *name) { + void *out; + MwVaWidgetExecute(handle, "mwOpenGLGetProcAddress", &out, name); + return out; } /*! @@ -56,7 +63,7 @@ MwInline void* MwOpenGLGetProcAddress(MwWidget handle, const char* name) { * @param handle Widget */ MwInline void MwOpenGLSwapBuffer(MwWidget handle) { - MwVaWidgetExecute(handle, "mwOpenGLSwapBuffer", NULL); + MwVaWidgetExecute(handle, "mwOpenGLSwapBuffer", NULL); } #ifdef __cplusplus diff --git a/milsko.xml b/milsko.xml index dc980cd8..bc73f0e2 100644 --- a/milsko.xml +++ b/milsko.xml @@ -353,7 +353,7 @@ - + diff --git a/pl/rules.pl b/pl/rules.pl index 59605a80..a35a6c3e 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -37,6 +37,7 @@ if (grep(/^wayland$/, @backends)) { } scan_wayland_protocol("stable", "xdg-shell", ""); + scan_wayland_protocol("stable", "viewporter", ""); scan_wayland_protocol("stable", "tablet", "-v2"); scan_wayland_protocol("staging", "xdg-toplevel-icon", "-v1"); scan_wayland_protocol("staging", "cursor-shape", "-v1"); @@ -50,7 +51,14 @@ if (grep(/^cocoa$/, @backends)) { add_cflags("-DUSE_COCOA"); new_object("src/backend/cocoa.m"); - $gl_libs = "-lGL -lGLU"; + if (param_get("opengl")) { + new_object("src/widget/opengl_cocoa.m"); + } + + # tim cook my man literally everybody and their mother who knows what opengl is knows its deprecated on macos and i'm not having you spam the console with this + add_cflags("-DGL_SILENCE_DEPRECATION"); + + $gl_libs = "-framework OpenGL"; } if (param_get("stb-image")) { @@ -76,8 +84,9 @@ if (param_get("vulkan") && param_get("vulkan-string-helper")) { } new_object("src/icon/*.c"); -new_object("src/font/*.c"); new_object("src/cursor/*.c"); +new_object("src/text/*.c"); +new_object("src/text/font/*.c"); new_object("src/widget/box.c"); new_object("src/widget/button.c"); diff --git a/src/backend/cocoa.m b/src/backend/cocoa.m index 42c38a57..725642d3 100644 --- a/src/backend/cocoa.m +++ b/src/backend/cocoa.m @@ -1,8 +1,35 @@ +#include "Mw/BaseTypes.h" #include -#include +#ifdef __clang__ +#pragma clang push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif -#include "../../external/stb_ds.h" +static NSRect rectFlip(NSRect originFrame) { + NSScreen* zeroScreen = [[NSScreen screens] objectAtIndex:0]; + double screenHeight = [zeroScreen frame].size.height; + double originY = originFrame.origin.y; + double frameHeight = originFrame.size.height; + double destinationY = screenHeight - (originY + frameHeight); + NSRect destinationFrame = originFrame; + destinationFrame.origin.y = destinationY; + return destinationFrame; +} + +static NSPoint pointFlip(NSPoint point) { + return rectFlip(NSMakeRect(point.x, point.y, 0, 0)).origin; +} + +static NSRect localRectFlip(NSRect originFrame, NSView* view) { + float viewHeight = [view bounds].size.height; + NSRect destinationFrame = NSMakeRect( + originFrame.origin.x, + [view bounds].size.height - (originFrame.origin.y + originFrame.size.height), + originFrame.size.width, + originFrame.size.height); + return destinationFrame; +} @implementation MilskoCocoaPixmap @@ -11,24 +38,53 @@ p->width = width; p->height = height; - p->data = NULL; p->image = NULL; + p->buf = malloc(width * height * 4); + + p->rep = + [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&p->buf + pixelsWide:(int)width + pixelsHigh:(int)height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:(int)width * 4 + bitsPerPixel:32]; + assert(p->rep); + + p->image = [[NSImage alloc] initWithSize:NSMakeSize(width, height)]; + assert(p->image); + [p->image addRepresentation:p->rep]; + return p; } -- (void)updateWithData:(void*)_data { - [self destroy]; - - self->data = [NSData dataWithBytes:_data length:self->width * self->height * 4]; - self->image = [[NSImage alloc] initWithData:self->data]; +- (void)updateWithData:(unsigned char*)_data { + memcpy(self->buf, _data, width * height * 4); + if(self->rep) { + [self->image removeRepresentation:self->rep]; + [self->rep release]; + } + self->rep = + [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&self->buf + pixelsWide:(int)width + pixelsHigh:(int)height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:(int)width * 4 + bitsPerPixel:32]; + [self->image addRepresentation:self->rep]; } - (void)destroy { - if(self->data != NULL) { - [self->data dealloc]; - } - if(self->image != NULL) { - [self->image dealloc]; - } + free(self->buf); + [self->image removeRepresentation:self->rep]; + [self->image dealloc]; + [self->rep dealloc]; } - (NSImage*)image { return self->image; @@ -41,110 +97,498 @@ x:(int)x y:(int)y width:(int)width - height:(int)height { - MilskoCocoa* c = [MilskoCocoa alloc]; - bool centerX = false, centerY = false; + height:(int)height + handle:(MwLL)r { + MilskoCocoa* c = [MilskoCocoa alloc]; + [c retain]; + /* + * MacOS doesn't really have a "default" window position, you're actually meant to center the window yourself, + * so if the user passes MwDEFAULT respond appropriately. Also for child windows just have it be 0. + */ if(x == MwDEFAULT) { - x = 0; - centerX = true; + x = r ? ([[NSScreen mainScreen] frame].size.width / 2.) - (width / 2.) : 0; } if(y == MwDEFAULT) { - y = 0; - centerY = true; + y = r ? ([[NSScreen mainScreen] frame].size.height / 2.) - (height / 2.) : 0; } c->application = [NSApplication sharedApplication]; + c->rect = NSMakeRect(x, y, width, height); - c->rect = NSMakeRect(x, y, width, height); + if(parent == NULL) { + c->window = [[MilskoCocoaWindow alloc] + initWithContentRect:rectFlip(c->rect) + styleMask:(NSTitledWindowMask | + NSTexturedBackgroundWindowMask | + NSClosableWindowMask | NSMiniaturizableWindowMask | + NSResizableWindowMask) + backing:NSBackingStoreBuffered + defer:NO]; + [c->window + setDelegate:[[MilskoCocoaWindowDelegate alloc] initWithWin:c->window]]; + [c->window retain]; - c->window = [[NSWindow alloc] - initWithContentRect:c->rect - styleMask:parent == NULL - ? (NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask | - NSResizableWindowMask) - : NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]; - [c->window makeKeyAndOrderFront:c->application]; + [c->window makeKeyAndOrderFront:c->application]; + [c->window makeFirstResponder:c->view]; + [c->window setAcceptsMouseMovedEvents:MwTRUE]; - if(parent != NULL) { - MilskoCocoa* p = parent->cocoa.real; - [p->window addChildWindow:c->window ordered:NSWindowAbove]; - } else { + c->view = [[MilskoCocoaView alloc] initWithFrame:c->rect]; + [c->view retain]; + [c->window setContentView:c->view]; + + if([c->application respondsToSelector:@selector(setActivationPolicy:)]) { + [c->application + setActivationPolicy:0 /* NSApplicationActivationPolicyRegular */]; + } [c->application activateIgnoringOtherApps:true]; + [c->application setDelegate:[[MilskoCocoaApplicationDelegate alloc] + initWithAppl:c->application]]; + [c->application retain]; + } else { + MilskoCocoa* p = parent->cocoa.real; + + c->window = p->window; + + if(parent) { + MilskoCocoa* topmost = p; + NSRect rect = localRectFlip(c->rect, p->view); + printf("%0.2f %0.2f %0.2f %0.2f\n", c->rect.origin.x, c->rect.origin.y, c->rect.size.width, c->rect.size.height); + c->view = [[MilskoCocoaView alloc] initWithFrame:rect]; + [c->view setBounds:c->rect]; + } else { + c->view = [[MilskoCocoaView alloc] initWithFrame:c->rect]; + } + [c->view retain]; + [c->view setNeedsDisplay:TRUE]; + [p->view addSubview:c->view]; + [p->window setContentView:p->view]; } - c->view = [[MilskoCocoaView alloc] initWithFrame:c->rect]; - [c->window setContentView:c->view]; + c->handle = [[MilskoFakePointer alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)]; + [c->handle retain]; + [c->handle setPointer:r]; + [c->view addSubview:c->handle]; + + c->modalSession = [c->application beginModalSessionForWindow:c->window]; + c->parent = parent; + + c->_forceRender = MwTRUE; + c->strHash = 0; + + c->cursorPixmap = NULL; + + [c->application finishLaunching]; + + c->pointerLocked = MwFALSE; return c; } + - (void)polygonWithPoints:(MwPoint*)points points_count:(int)points_count color:(MwLLColor)color { - int i; - CGContextRef cg = [self->view context]; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSGraphicsContext* ctx = [self->view context]; + if(ctx) { + int i; + NSBezierPath* path = [NSBezierPath bezierPath]; + /* whatever rect we use for coordinate flipping */ + NSRect _rect = parent ? [self->view bounds] : [self->window frame]; - CGContextSetRGBFillColor(cg, color->common.red / 255., color->common.blue / 255., color->common.green / 255., 1); - CGContextBeginPath(cg); - for(i = 0; i < points_count; i++) { - CGContextMoveToPoint(cg, points[i].x, points[i].y); - if(i < points_count - 1) { - CGContextAddLineToPoint(cg, points[i + 1].x, points[i + 1].y); + NSColor* nscolor = + [NSColor colorWithCalibratedRed:color->common.red / 255. + green:color->common.green / 255. + blue:color->common.blue / 255. + alpha:1.0]; + + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:ctx]; + + [nscolor setFill]; + NSRectFill(self->view.bounds); + for(i = 0; i < points_count; i++) { + if(i == 0) { + [path moveToPoint:NSMakePoint(points[i].x, _rect.size.height - + points[i].y)]; + } else { + [path lineToPoint:NSMakePoint(points[i].x, _rect.size.height - + points[i].y)]; + } } - } - CGContextFillPath(cg); - // [self->view setNeedsDisplay:true]; + [path closePath]; + [path fill]; + + [NSGraphicsContext restoreGraphicsState]; + + [self->view setNeedsDisplay:YES]; + } + [pool release]; }; - (void)lineWithPoints:(MwPoint*)points color:(MwLLColor)color { + (void)points; + (void)color; }; - (void)getX:(int*)x Y:(int*)y W:(unsigned int*)w H:(unsigned int*)h { - NSRect frame = [self->window frame]; + NSRect frame; + if(parent) { + frame = [self->view frame]; + } else { + frame = [self->window frame]; + } + frame = rectFlip(frame); *x = frame.origin.x; *y = frame.origin.y; + *w = frame.size.width; *h = frame.size.height; }; - (void)setX:(int)x Y:(int)y { - NSPoint p; - p.x = x; - p.y = y; - NSRect frame = [self->window frame]; + NSRect frame; + if(parent) { + frame = [self->view frame]; + } else { + frame = [self->window frame]; + } + frame.origin.x = x; frame.origin.y = y; - [self->window setFrame:frame display:YES animate:true]; + + if(!parent) { + frame = rectFlip(frame); + [self->window setFrame:frame display:TRUE animate:TRUE]; + } else { + frame = localRectFlip(frame, parent->cocoa.real->view); + [self->view setBounds:frame]; + } }; - (void)setW:(int)w H:(int)h { NSRect frame = [self->window frame]; frame.size.width = w; frame.size.height = h; - [self->window setFrame:frame display:YES animate:true]; + + self->rect = frame; + + [self->view setFrameSize:frame.size]; + if(self->parent) { + [self->view setFrame:frame]; + } else { + [self->window setFrame:frame display:YES animate:false]; + } }; - (int)pending { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + [NSApp runModalSession:self->modalSession]; + [pool release]; return 1; }; + - (void)getNextEvent { - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + [self eventProcess:self->lastEvent]; - NSEvent* event = [self->application nextEventMatchingMask:NSAnyEventMask - untilDate:nil - inMode:NSDefaultRunLoopMode - dequeue:YES]; + if(_forceRender) { + MwLL h = [self->handle pointer]; + MwLLDispatch(h, draw, NULL); + [self->view setNeedsDisplay:true]; + _forceRender = MwFALSE; + } + [self sendClipboardEvent]; - if(event != nil) { - printf("got event: %p\n", event); + if(self->pointerLocked && [self->window isMainWindow] && self->lastEvent) { + struct CGPoint pos; + pos.x = [window frame].origin.x + [window frame].size.width / 2; + pos.y = [window frame].origin.y + [window frame].size.height / 2; + + CGWarpMouseCursorPosition(pos); } - [self->application sendEvent:event]; - - /* this should be in the draw functions but it's here for now for testing */ - [self->view setNeedsDisplay:true]; - - [pool release]; + [self->application updateWindows]; }; + +- (void)eventProcess:(NSEvent*)ev { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = [ev window]; + MwLL h; + MwBool doSendEvent = MwTRUE; + + if(!win) { + [pool release]; + return; + } + + if([[[win contentView] subviews] count] == 0) { + printf("no subviews on %p\n", win); + [pool release]; + return; + } else { + h = [((MilskoFakePointer*)[[[win contentView] subviews] + objectAtIndex:0]) pointer]; + } + + switch([ev type]) { + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + case NSMouseMoved: { + MwPoint pos; + NSPoint pos_translated = pointFlip([ev locationInWindow]); + pos.x = pos_translated.x; + pos.y = pos_translated.y; + MwLLDispatch(h, move, &pos); + break; + } + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: { + [self handleMouseEvent:ev ll:h]; + break; + } + + case NSMouseEntered: + MwLLDispatch(h, focus_in, NULL); + [self->cursor push]; + break; + case NSMouseExited: + MwLLDispatch(h, focus_out, NULL); + [self->cursor pop]; + break; + case NSKeyDown: + case NSKeyUp: { + [self handleKeyEvent:ev ll:h]; + break; + } + case NSCursorUpdate: + [self->cursor set]; + break; + case NSScrollWheel: + break; + default: + /* mute bizarre "unknown subtype" errors that flood the console */ + break; + }; + if(doSendEvent) { + [win sendEvent:ev]; + } + [pool release]; +} + +- (void)handleMouseEvent:(NSEvent*)ev ll:(MwLL)ll { + MwLLMouse mouse; + MwBool isDown = MwTRUE; + NSPoint mousePoint = [ev locationInWindow]; + mousePoint.y = [[ev window] frame].size.height - mousePoint.y; + MwLL this = [self->handle pointer]; + + switch([ev type]) { + case NSLeftMouseUp: + isDown = MwFALSE; + case NSEventTypeLeftMouseDragged: + case NSLeftMouseDown: + mouse.button = MwLLMouseLeft; + break; + case NSRightMouseUp: + isDown = MwFALSE; + case NSRightMouseDown: + case NSEventTypeRightMouseDragged: + mouse.button = MwLLMouseRight; + break; + case NSOtherMouseUp: + isDown = MwFALSE; + case NSOtherMouseDown: + case NSEventTypeOtherMouseDragged: + mouse.button = MwLLMouseMiddle; + break; + default: + break; + } + mouse.point.x = mousePoint.x; + mouse.point.y = mousePoint.y; + + printf("%s at %d %d\n", isDown ? "down" : "up", mouse.point.x, mouse.point.y); + + if(isDown) { + MwLLDispatch(ll, down, &mouse); + } else { + MwLLDispatch(ll, up, &mouse); + } +} + +- (void)handleKeyEvent:(NSEvent*)ev ll:(MwLL)ll { + int ch; + MwLL this = [self->handle pointer]; + enum { + kVK_ANSI_A = 0x00, + kVK_ANSI_S = 0x01, + kVK_ANSI_D = 0x02, + kVK_ANSI_F = 0x03, + kVK_ANSI_H = 0x04, + kVK_ANSI_G = 0x05, + kVK_ANSI_Z = 0x06, + kVK_ANSI_X = 0x07, + kVK_ANSI_C = 0x08, + kVK_ANSI_V = 0x09, + kVK_ANSI_B = 0x0B, + kVK_ANSI_Q = 0x0C, + kVK_ANSI_W = 0x0D, + kVK_ANSI_E = 0x0E, + kVK_ANSI_R = 0x0F, + kVK_ANSI_Y = 0x10, + kVK_ANSI_T = 0x11, + kVK_ANSI_1 = 0x12, + kVK_ANSI_2 = 0x13, + kVK_ANSI_3 = 0x14, + kVK_ANSI_4 = 0x15, + kVK_ANSI_6 = 0x16, + kVK_ANSI_5 = 0x17, + kVK_ANSI_Equal = 0x18, + kVK_ANSI_9 = 0x19, + kVK_ANSI_7 = 0x1A, + kVK_ANSI_Minus = 0x1B, + kVK_ANSI_8 = 0x1C, + kVK_ANSI_0 = 0x1D, + kVK_ANSI_RightBracket = 0x1E, + kVK_ANSI_O = 0x1F, + kVK_ANSI_U = 0x20, + kVK_ANSI_LeftBracket = 0x21, + kVK_ANSI_I = 0x22, + kVK_ANSI_P = 0x23, + kVK_ANSI_L = 0x25, + kVK_ANSI_J = 0x26, + kVK_ANSI_Quote = 0x27, + kVK_ANSI_K = 0x28, + kVK_ANSI_Semicolon = 0x29, + kVK_ANSI_Backslash = 0x2A, + kVK_ANSI_Comma = 0x2B, + kVK_ANSI_Slash = 0x2C, + kVK_ANSI_N = 0x2D, + kVK_ANSI_M = 0x2E, + kVK_ANSI_Period = 0x2F, + kVK_ANSI_Grave = 0x32, + kVK_Return = 0x24, + kVK_Space = 0x31, + kVK_Escape = 0x35, + kVK_Shift = 0x38, + kVK_Control = 0x3B, + kVK_RightShift = 0x3C, + kVK_RightControl = 0x3E, + kVK_LeftArrow = 0x7B, + kVK_RightArrow = 0x7C, + kVK_DownArrow = 0x7D, + kVK_UpArrow = 0x7E + }; +#define KEY_CASE(x, y) \ + case x: \ + ch = y; \ + break; + switch([ev keyCode]) { + KEY_CASE(kVK_ANSI_A, 'a') + KEY_CASE(kVK_ANSI_B, 'b') + KEY_CASE(kVK_ANSI_C, 'c') + KEY_CASE(kVK_ANSI_D, 'd') + KEY_CASE(kVK_ANSI_E, 'e') + KEY_CASE(kVK_ANSI_F, 'f') + KEY_CASE(kVK_ANSI_G, 'g') + KEY_CASE(kVK_ANSI_H, 'h') + KEY_CASE(kVK_ANSI_I, 'i') + KEY_CASE(kVK_ANSI_J, 'j') + KEY_CASE(kVK_ANSI_K, 'k') + KEY_CASE(kVK_ANSI_L, 'l') + KEY_CASE(kVK_ANSI_M, 'm') + KEY_CASE(kVK_ANSI_N, 'n') + KEY_CASE(kVK_ANSI_O, 'o') + KEY_CASE(kVK_ANSI_P, 'p') + KEY_CASE(kVK_ANSI_Q, 'q') + KEY_CASE(kVK_ANSI_R, 'r') + KEY_CASE(kVK_ANSI_S, 's') + KEY_CASE(kVK_ANSI_T, 't') + KEY_CASE(kVK_ANSI_U, 'u') + KEY_CASE(kVK_ANSI_V, 'v') + KEY_CASE(kVK_ANSI_W, 'w') + KEY_CASE(kVK_ANSI_X, 'x') + KEY_CASE(kVK_ANSI_Y, 'y') + KEY_CASE(kVK_ANSI_Z, 'z') + KEY_CASE(kVK_ANSI_0, '0') + KEY_CASE(kVK_ANSI_1, '1') + KEY_CASE(kVK_ANSI_2, '2') + KEY_CASE(kVK_ANSI_3, '3') + KEY_CASE(kVK_ANSI_4, '4') + KEY_CASE(kVK_ANSI_5, '5') + KEY_CASE(kVK_ANSI_6, '6') + KEY_CASE(kVK_ANSI_7, '7') + KEY_CASE(kVK_ANSI_8, '8') + KEY_CASE(kVK_ANSI_9, '9') + KEY_CASE(kVK_ANSI_Quote, '\"') + KEY_CASE(kVK_ANSI_Grave, '`') + KEY_CASE(kVK_ANSI_Backslash, '/') + KEY_CASE(kVK_ANSI_Comma, ',') + KEY_CASE(kVK_ANSI_Equal, '=') + KEY_CASE(kVK_Escape, MwLLKeyEscape) + KEY_CASE(kVK_ANSI_LeftBracket, '[') + KEY_CASE(kVK_ANSI_Minus, '-') + KEY_CASE(kVK_ANSI_Period, '.') + KEY_CASE(kVK_Return, MwLLKeyEnter) + KEY_CASE(kVK_ANSI_RightBracket, ']') + KEY_CASE(kVK_ANSI_Semicolon, ';') + KEY_CASE(kVK_ANSI_Slash, '\\') + KEY_CASE(kVK_Space, ' ') + KEY_CASE(kVK_Control, MwLLKeyControl) + KEY_CASE(kVK_RightControl, MwLLKeyControl) + KEY_CASE(kVK_Shift, MwLLKeyLeftShift) + KEY_CASE(kVK_RightShift, MwLLKeyRightShift) + KEY_CASE(kVK_DownArrow, MwLLKeyDown) + KEY_CASE(kVK_LeftArrow, MwLLKeyLeft) + KEY_CASE(kVK_RightArrow, MwLLKeyRight) + KEY_CASE(kVK_UpArrow, MwLLKeyUp) + } + switch([ev type]) { + case NSKeyDown: + MwLLDispatch(this, key, &ch); + MwLLDispatch(ll, key, &ch); + break; + case NSKeyUp: + MwLLDispatch(this, key_released, &ch); + MwLLDispatch(ll, key_released, &ch); + break; + default: + break; + } +} + +- (void)sendClipboardEvent { + /*NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + NSArray* items = @[ + @"public.utf8-plain-text", + @"public.utf16-external-plain-text", + @"com.apple.traditional-mac-plain-text", + ]; + MwLL this = self->handle.pointer; + + if([pasteboard canReadItemWithDataConformingToTypes:items]) { + char* data = NULL; + size_t size = 0; + for(NSPasteboardItem* item in [pasteboard pasteboardItems]) { + for(NSString* it in items) { + NSString* itemData = [item + stringForType:(NSString*)it]; if(itemData != NULL) { if(strHash != 0 && + strHash != [itemData hash]) { char* text = malloc([itemData length]); + strncpy(text, [itemData UTF8String], + [itemData length]); MwLLDispatch(this, clipboard, text); printf("%s -> %p\n", + text, this); free(text); + } + strHash = [itemData hash]; + } + } + } + } + + [pool release];*/ +} + - (void)setTitle:(const char*)title { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; [self->window @@ -152,29 +596,119 @@ [pool release]; }; - (void)drawPixmap:(MwLLPixmap)pixmap rect:(MwRect*)_rect { - MilskoCocoaPixmap* p = pixmap->cocoa.real; - [[p image] drawAtPoint:NSMakePoint(_rect->x, _rect->y) fromRect:NSMakeRect(_rect->x, _rect->y, _rect->width, _rect->height) operation:NSCompositeClear fraction:1.0]; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MilskoCocoaPixmap* p = pixmap->cocoa.real; + NSGraphicsContext* ctx = [self->view context]; + if(ctx) { + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:ctx]; + + [[p image] + drawInRect:NSMakeRect(_rect->x, _rect->y, _rect->width, _rect->height) + fromRect:NSZeroRect + operation:NSCompositeSourceOver + fraction:1.0]; + + [NSGraphicsContext restoreGraphicsState]; + + [self->view setNeedsDisplay:YES]; + } + [pool release]; }; - (void)setIcon:(MwLLPixmap)pixmap { + [self->application setApplicationIconImage:[pixmap->cocoa.real image]]; }; - (void)forceRender { + self->_forceRender = MwTRUE; + // NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + // NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined + // location:NSMakePoint(0, 0) + // modifierFlags:0 + // timestamp:0 + // windowNumber:0 + // context:nil + // subtype:0 + // data1:0 + // data2:0]; + // [NSApp postEvent:event atStart:YES]; + // [pool release]; }; - (void)setCursor:(MwCursor*)image mask:(MwCursor*)mask { + int y, x, ys, xs; + unsigned char* di = malloc(image->width * image->height * 4); + memset(di, 0, image->width * image->height * 4); + + if(self->cursorPixmap) { + [self->cursorPixmap destroy]; + } + self->cursorPixmap = + [MilskoCocoaPixmap newWithWidth:image->width + height:image->height]; + + xs = -mask->x + image->x; + ys = MwCursorDataHeight + mask->y; + ys = MwCursorDataHeight + image->y - ys; + + for(y = 0; y < mask->height; y++) { + unsigned int d = mask->data[y]; + for(x = mask->width - 1; x >= 0; x--) { + int px = 0; + int idx = ((y * mask->width) + x) * 4; + + if(d & 1) { + di[idx + 3] = 255; + }; + d = d >> 1; + } + } + for(y = 0; y < image->height; y++) { + unsigned int d = image->data[y]; + for(x = image->width - 1; x >= 0; x--) { + int px = 0; + int idx = ((y * image->width) + x) * 4; + + if(d & 1) { + px = 255; + }; + + di[idx] = px; + di[idx + 1] = px; + di[idx + 2] = px; + d = d >> 1; + } + } + + [self->cursorPixmap updateWithData:di]; + + self->cursor = [[NSCursor alloc] + initWithImage:[self->cursorPixmap image] + hotSpot:NSMakePoint(image->x, image->y + image->height)]; + [self->cursor retain]; + + [self->cursor pop]; + [self->cursor push]; + + free(di); }; - (void)detachWithPoint:(MwPoint*)point { + [self->window setParentWindow:NULL]; }; - (void)show:(int)show { + (void)show; }; -- (void)makePopupWithParent:(MwLL)parent { +- (void)makePopupWithParent:(MwLL)_parent { + (void)_parent; }; - (void)setSizeHintsWithMinX:(int)minx MinY:(int)miny MaxX:(int)maxx MaxY:(int)maxy { + [self->window setMinSize:NSMakeSize(minx, miny)]; + [self->window setMaxSize:NSMakeSize(maxx, maxy)]; }; - (void)makeBorderless:(int)toggle { MwU32 mask = [self->window styleMask]; - if(mask & NSBorderlessWindowMask) { + if(toggle) { mask ^= NSBorderlessWindowMask; mask |= NSTitledWindowMask; } else { @@ -190,14 +724,23 @@ [self->window makeMainWindow]; }; - (void)grabPointer:(int)toggle { - /* MacOS didn't have a "pointer grab" function until 10.13.2 so I need to do - * this manually */ + self->pointerLocked = toggle; }; - (void)setClipboard:(const char*)text { -}; -- (void)getClipboard { + (void)text; + // TODO: find out how to do this while supporting 10.4 + // NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + // NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + // [pasteboard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeString] + // owner:nil]; [pasteboard setString:[NSString stringWithUTF8String:text] + // forType:NSPasteboardTypeString]; [pool release]; }; - (void)makeToolWindow { + /* If my understand of what a "tool window" usually is is correct then I + * highly doubt the Mac OS has this and if they did they probably outright + * removed it along time ago is this kind of conflicts with modern UX. So + * we'll just make it borderless idgaf */ + [self makeBorderless:MwTRUE]; }; - (void)getCursorCoord:(MwPoint*)point { NSPoint p = [NSEvent mouseLocation]; @@ -212,75 +755,238 @@ _rect->height = [screen frame].size.height; }; - (void)destroy { + [self->handle release]; + [self->handle dealloc]; + [self->window release]; [self->window dealloc]; } +- (MwLL)getParent { + return self->parent; +} + +- (NSView*)getView { + return view; +} +- (NSWindow*)getWindow { + return window; +} + +- (MilskoFakePointer*)getHandle { + return handle; +} + @end @implementation MilskoCocoaView - (id)initWithFrame:(NSRect)frame { - self = [super initWithFrame:frame]; - self->width = frame.size.width; - self->height = frame.size.height; - self->buf = malloc(self->width * self->height * 4); - self->space = CGColorSpaceCreateDeviceRGB(); - self->cg = CGBitmapContextCreate(self->buf, - self->width, - self->height, - CHAR_BIT, - self->width * sizeof(MwU32), - self->space, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedLast); - assert(self->cg); - printf("%p\n", self->cg); + width = frame.size.width; + height = frame.size.height; + self = [super initWithFrame:frame]; + self->space = CGColorSpaceCreateDeviceRGB(); + + if(width == 0 || height == 0) { + self->rep = NULL; + self->context = NULL; + } else { + [self initRepAndContextWithWidth:width Height:height]; + } + return self; } -- (CGContextRef)context { - return self->cg; +- (NSGraphicsContext*)context { + return self->context; +} + +- (NSBitmapImageRep*)getRep { + return self->rep; } - (void)drawRect:(NSRect)dirtyRect { - [super drawRect:dirtyRect]; - if(self) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSSize sz = [self->rep size]; + MwLL ll = [((MilskoFakePointer*)[[[[self window] contentView] subviews] + objectAtIndex:0]) pointer]; - CGImageRef img = CGBitmapContextCreateImage(self->cg); - if(!img) { + [super drawRect:dirtyRect]; + if(!self->rep) { + if(dirtyRect.size.width && dirtyRect.size.height) { + [self initRepAndContextWithWidth:dirtyRect.size.width + Height:dirtyRect.size.height]; + self->width = dirtyRect.size.width; + self->height = dirtyRect.size.height; + } else { + [pool release]; return; } - printf("printed image\n"); - - CGContextDrawImage([[NSGraphicsContext currentContext] graphicsPort], CGRectMake(0, 0, self->width, self->height), img); - CGImageRelease(img); } - // printf("%0.2f %0.2f %0.2f %0.2f\n", dirtyRect.origin.x, dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height); + + // MwLLDispatch(ll, draw, NULL); + + [self->rep drawInRect:dirtyRect]; + + for(NSView* subview in [self subviews]) { + NSRect bounds = [subview bounds]; + [subview drawRect:bounds]; + } + + [self->rep bitmapData]; + + [pool release]; +} + +- (void)initRepAndContextWithWidth:(float)w Height:(float)h { + self->rep = + [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL + pixelsWide:w + pixelsHigh:h + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:w * 4 + bitsPerPixel:32]; + assert(self->rep); + [self->rep retain]; + + self->context = + [NSGraphicsContext graphicsContextWithBitmapImageRep:self->rep]; + assert(self->context); + [self->context retain]; } - (void)destroy { - free(self->buf); - CGContextRelease(self->cg); CGColorSpaceRelease(self->space); + [self->rep release]; + [self->context release]; +} + +- (void)setFrameSize:(NSSize)newSize { + [super setFrameSize:newSize]; + [self->rep setSize:newSize]; + + self->width = newSize.width; + self->height = newSize.height; +} + +- (void)displayRect:(NSRect)rect { + (void)rect; +}; + +- (BOOL)acceptsFirstResponder { + return MwTRUE; +} +- (BOOL)performKeyEquivalent:(NSEvent*)event { + (void)event; + return MwTRUE; +} + +@end + +@implementation MilskoCocoaApplicationDelegate +- (MilskoCocoaApplicationDelegate*)initWithAppl:(NSApplication*)_appl { + self->appl = _appl; + return self; +} +- (void)applicationDidBecomeActive:(NSNotification*)notification { + [self->appl activateIgnoringOtherApps:true]; + // printf("test\n"); +} +- (void)applicationWillFinishLaunching:(NSNotification*)notification { + // printf("test\n"); + // [self->appl activateIgnoringOtherApps:MwTRUE]; +} +- (void)applicationDidFinishLaunching:(NSNotification*)notification { + // [self->appl activateIgnoringOtherApps:MwTRUE]; +} +@end + +@implementation MilskoCocoaWindowDelegate + +- (NSSize)windowWillResize:(NSWindow*)win toSize:(NSSize)frameSize; +{ + if([[[win contentView] subviews] count] >= 1) { + MilskoFakePointer* ptr = [[[win contentView] subviews] objectAtIndex:0]; + MwLL h = [ptr pointer]; + + MwLLDispatch(h, resize, NULL); + MwLLDispatch(h, draw, NULL); + } + return frameSize; +} + +- (void)windowDidBecomeMain:(NSNotification*)notification { + if([[[w contentView] subviews] count] != 0) { + MwLL h = [((MilskoFakePointer*)[[[w contentView] subviews] + objectAtIndex:0]) pointer]; + MwLLDispatch(h, focus_in, NULL); + } + [self->w makeKeyAndOrderFront:nil]; +} + +- (void)windowDidResize:(NSNotification*)notification { + (void)notification; +} + +// This will close/terminate the application when the main window is closed. +- (void)windowWillClose:(NSNotification*)notification { + (void)notification; + // MilskoCocoa *window = notification.object; + // MwLL handle = [window getHandle].pointer; + // MwLLDispatch(handle, close, NULL); + [NSApp terminate:nil]; +} + +- (MilskoCocoaWindowDelegate*)initWithWin:(NSWindow*)win { + self->w = win; + return self; +} + +@end + +@implementation MilskoFakePointer +- (void)setPointer:(void*)pointer { + [self setFrame:*(NSRect*)&pointer]; + self->ptr = pointer; +}; +- (void*)pointer { + return self->ptr; +}; + +- (void)drawRect:(NSRect)dirtyRect { + /* explicitly do nothing */ + (void)dirtyRect; +} + +- (void)destroy { +} +@end + +@implementation MilskoCocoaWindow +- (BOOL)canBecomeKeyWindow { + return true; +} +- (BOOL)canBecomeMainWindow { + return true; } @end static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { MwLL r; - (void)x; - (void)y; - (void)width; - (void)height; - r = malloc(sizeof(*r)); MwLLCreateCommon(r); - MilskoCocoa* o = - [MilskoCocoa newWithParent:parent - x:x - y:y - width:width - height:height]; - r->cocoa.real = o; + MilskoCocoa* o = [MilskoCocoa newWithParent:parent + x:x + y:y + width:width + height:height + handle:r]; + r->cocoa.real = o; return r; } @@ -349,12 +1055,14 @@ static void MwLLFreeColorImpl(MwLLColor color) { } static int MwLLPendingImpl(MwLL handle) { - MilskoCocoa* h = handle->cocoa.real; - return [h pending]; + int p = [handle->cocoa.real pending]; + if(p) { + MwLLDispatch(handle, draw, NULL); + } + return p; } static void MwLLNextEventImpl(MwLL handle) { - MilskoCocoa* h = handle->cocoa.real; [h getNextEvent]; } @@ -379,6 +1087,7 @@ static MwLLPixmap MwLLCreatePixmapImpl(MwLL handle, unsigned char* data, r->cocoa.real = [MilskoCocoaPixmap newWithWidth:width height:height]; MwLLPixmapUpdate(r); + free(r->common.raw); return r; } @@ -390,12 +1099,14 @@ static void MwLLPixmapUpdateImpl(MwLLPixmap pixmap) { static void MwLLDestroyPixmapImpl(MwLLPixmap pixmap) { MilskoCocoaPixmap* p = pixmap->cocoa.real; [p destroy]; + [p dealloc]; free(pixmap); } static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { MilskoCocoa* h = handle->cocoa.real; [h drawPixmap:pixmap rect:rect]; + MwLLForceRender(handle); } static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) { diff --git a/src/backend/gdi.c b/src/backend/gdi.c index 764e9838..4601b79d 100644 --- a/src/backend/gdi.c +++ b/src/backend/gdi.c @@ -8,6 +8,27 @@ typedef struct userdata { int max_set; } userdata_t; +static void detect_darktheme(MwLL handle) { + DWORD dw; + DWORD sz = sizeof(dw); + int err, t; + HKEY hkey; + DWORD type; + + err = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_QUERY_VALUE, &hkey); + if(err != ERROR_SUCCESS) return; + + err = RegQueryValueEx(hkey, "AppsUseLightTheme", NULL, &type, (PBYTE)&dw, &sz); + RegCloseKey(hkey); + if(err != ERROR_SUCCESS || type != REG_DWORD) { + return; + } + + t = dw ? 0 : 1; + + MwLLDispatch(handle, dark_theme, &t); +} + static LRESULT CALLBACK wndproc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { userdata_t* u = (userdata_t*)GetWindowLongPtr(hWnd, GWLP_USERDATA); @@ -205,6 +226,13 @@ static LRESULT CALLBACK wndproc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { } else if(msg == WM_USER) { InvalidateRect(hWnd, NULL, FALSE); UpdateWindow(hWnd); + } else if(msg == WM_WININICHANGE) { + char* s = (char*)lp; + LPARAM style = GetWindowLongPtr(hWnd, GWL_STYLE); + + if(!(style & WS_CHILD)) { + if(s != NULL && strcmp(s, "ImmersiveColorSet") == 0) detect_darktheme(u->ll); + } } else { return DefWindowProc(hWnd, msg, wp, lp); } @@ -238,12 +266,13 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { r->common.type = MwLLBackendGDI; r->gdi.get_clipboard = 1; - r->gdi.force_render = 0; - r->gdi.grabbed = 0; - r->gdi.hWnd = CreateWindow("milsko", "Milsko", parent == NULL ? (WS_OVERLAPPEDWINDOW) : (WS_CHILD | WS_VISIBLE), x == MwDEFAULT ? CW_USEDEFAULT : x, y == MwDEFAULT ? CW_USEDEFAULT : y, width, height, parent == NULL ? NULL : parent->gdi.hWnd, 0, wc.hInstance, NULL); - r->gdi.hInstance = wc.hInstance; - r->gdi.cursor = NULL; - r->gdi.icon = NULL; + if(parent == NULL) r->gdi.get_darktheme = 1; + r->gdi.force_render = 0; + r->gdi.grabbed = 0; + r->gdi.hWnd = CreateWindow("milsko", "Milsko", parent == NULL ? (WS_OVERLAPPEDWINDOW) : (WS_CHILD | WS_VISIBLE), x == MwDEFAULT ? CW_USEDEFAULT : x, y == MwDEFAULT ? CW_USEDEFAULT : y, width, height, parent == NULL ? NULL : parent->gdi.hWnd, 0, wc.hInstance, NULL); + r->gdi.hInstance = wc.hInstance; + r->gdi.cursor = NULL; + r->gdi.icon = NULL; u->ll = r; u->min_set = 0; @@ -384,7 +413,7 @@ static int MwLLPendingImpl(MwLL handle) { (void)handle; - if(handle->gdi.get_clipboard) return 1; + if(handle->gdi.get_clipboard || handle->gdi.get_darktheme) return 1; return PeekMessage(&msg, handle->gdi.hWnd, 0, 0, PM_NOREMOVE) ? 1 : 0; } @@ -411,6 +440,11 @@ static void MwLLNextEventImpl(MwLL handle) { handle->gdi.get_clipboard = 0; } + if(handle->gdi.get_darktheme) { + detect_darktheme(handle); + + handle->gdi.get_darktheme = 0; + } while(PeekMessage(&msg, handle->gdi.hWnd, 0, 0, PM_NOREMOVE)) { GetMessage(&msg, handle->gdi.hWnd, 0, 0); TranslateMessage(&msg); @@ -629,6 +663,8 @@ static void MwLLDetachImpl(MwLL handle, MwPoint* point) { rc2.bottom -= rc2.top; SetWindowPos(handle->gdi.hWnd, HWND_TOPMOST, rc.left, rc.top, rc2.right == 0 ? 1 : rc2.right, rc2.bottom == 0 ? 1 : rc2.bottom, SWP_FRAMECHANGED | SWP_NOACTIVATE); + + handle->gdi.get_darktheme = 1; } static void MwLLShowImpl(MwLL handle, int show) { diff --git a/src/backend/wayland.c b/src/backend/wayland.c index 7acc0389..2e32ceda 100644 --- a/src/backend/wayland.c +++ b/src/backend/wayland.c @@ -1,20 +1,11 @@ #include -#include -#include -#include +#include +#include #include "../../external/stb_ds.h" -#include "Mw/BaseTypes.h" -#include "Mw/LowLevel.h" - -#include -#include -#include -#include /* TODO: - * - MwLLGrabPointerImpl * - MwLLMakePopupImpl * - MwLLShowImpl */ @@ -48,6 +39,11 @@ static void framebuffer_setup(struct _MwLLWayland* wayland); /* Destroy the framebuffer */ static void framebuffer_destroy(struct _MwLLWayland* handle); +/* Setup the framebuffer with the saved width/height */ +static void backbuffer_setup(struct _MwLLWayland* wayland); +/* Destroy the backbuffer */ +static void backbuffer_destroy(struct _MwLLWayland* handle); + static void buffer_destroy(struct _MwLLWaylandShmBuffer* buffer); static void region_setup(MwLL handle); @@ -232,8 +228,12 @@ static void wl_clipboard_read(wl_clipboard_device_context_t* ctx) { arrpush(buf, 0); close(fds[0]); + printf("sending: %s\n", buf); + MwLLDispatch(ctx->ll, clipboard, buf); arrfree(buf); + + ctx->ll->wayland.events_pending = 1; } static void wl_data_source_listener_target(void* data, @@ -250,8 +250,6 @@ static void wl_data_source_listener_send(void* data, if(self->wayland.clipboard_buffer != NULL) { write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer)); close(fd); - - self->wayland.events_pending = 1; } WAYLAND_EVENT_OP_END(self); }; @@ -296,8 +294,6 @@ static void zwp_primary_selection_source_v1_send(void* data, if(self->wayland.clipboard_buffer != NULL) { write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer)); close(fd); - - self->wayland.events_pending = 1; } WAYLAND_EVENT_OP_END(self); @@ -385,6 +381,7 @@ static void pointer_enter(void* data, struct wl_pointer* wl_pointer, MwU32 seria MwLL self = data; WAYLAND_EVENT_OP_START(self); + self->wayland.curSurface = surface; self->wayland.pointer_serial = serial; @@ -398,6 +395,32 @@ static void pointer_leave(void* data, struct wl_pointer* wl_pointer, MwU32 seria struct wl_surface* surface) { }; +static void xdg_borderless_step(MwLL self, MwLLMouse p, MwU32 serial) { + if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL && self->wayland.backbuffer.surface == self->wayland.curSurface) { + if(p.point.y >= self->wayland.wh - 5) { + if(p.point.x >= self->wayland.ww - 5) { + xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT); + } else { + xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM); + } + } else if(p.point.y <= 5) { + if(p.point.x >= self->wayland.ww - 5) { + xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT); + } else { + xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_TOP); + } + } else if(p.point.x >= self->wayland.ww - 5) { + xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT); + } else if(p.point.x <= 5) { + xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_LEFT); + } else if(p.point.y <= CSD_BORDER_FRAME_TOP) { + xdg_toplevel_move(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial); + } + }; +} + +static MwLL currentlyHeldWidget = NULL; + /* `wl_pointer.motion` callback */ static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time, wl_fixed_t surface_x, wl_fixed_t surface_y) { @@ -406,23 +429,33 @@ static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time WAYLAND_EVENT_OP_START(self); - self->wayland.cur_mouse_pos.x = wl_fixed_to_double(surface_x); - self->wayland.cur_mouse_pos.y = wl_fixed_to_double(surface_y); - - p.point = self->wayland.cur_mouse_pos; + self->wayland.cur_mouse_pos.x = wl_fixed_to_int(surface_x); + self->wayland.cur_mouse_pos.y = wl_fixed_to_int(surface_y); + p.point = self->wayland.cur_mouse_pos; MwLLDispatch(self, move, &p); - /* Only draw once every 50 milliseconds */ - if((self->wayland.last_time + 50) <= time) { - MwLLDispatch(self, draw, NULL); - self->wayland.last_time = time; + if(currentlyHeldWidget) { + MwLLDispatch(currentlyHeldWidget, down, &p); } - self->wayland.events_pending += 1; + /* We want to send a draw call whenever we move the cursor, BUT only if the topmost parent doesn't already have events pedngin. */ + if(self->wayland.parent) { + MwLL topmost = self->wayland.parent; + while(topmost->wayland.parent) topmost = topmost->wayland.parent; + /* also we only wanna do it every 50ms */ + if((self->wayland.last_time + 50) <= time && !topmost->wayland.events_pending) { + MwLLDispatch(self, draw, NULL); + self->wayland.last_time = time; + } + } else { + /* if there's no parent just impose the requirement on the widget itself */ + if((self->wayland.last_time + 50) <= time && !self->wayland.events_pending) { + MwLLDispatch(self, draw, NULL); + self->wayland.last_time = time; + } + } WAYLAND_EVENT_OP_END(self); - - /*timed_redraw(self, time, 50, &self->wayland.cooldown_timer);*/ }; /* `wl_pointer.button` callback */ @@ -430,17 +463,23 @@ static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 seri MwLL self = data; MwLLMouse p; + int x = self->wayland.x; + int y = self->wayland.y; + WAYLAND_EVENT_OP_START(self); p.point = self->wayland.cur_mouse_pos; - if(p.point.x > self->wayland.x && p.point.x < self->wayland.x + self->wayland.ww && p.point.y > self->wayland.y && p.point.y < self->wayland.y + self->wayland.wh) { + + if(self->wayland.framebuffer.surface == self->wayland.curSurface || self->wayland.backbuffer.surface == self->wayland.curSurface) { + int i; + switch(button) { case BTN_LEFT: p.button = MwLLMouseLeft; break; case BTN_MIDDLE: p.button = MwLLMouseMiddle; - for(int i = 0; i < arrlen(self->wayland.clipboard_devices); i++) { + for(i = 0; i < arrlen(self->wayland.clipboard_devices); i++) { wl_clipboard_read( self->wayland.clipboard_devices[i]); } @@ -449,19 +488,18 @@ static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 seri p.button = MwLLMouseRight; break; } + self->wayland.held_down = state == WL_POINTER_BUTTON_STATE_PRESSED; + switch(state) { case WL_POINTER_BUTTON_STATE_PRESSED: MwLLDispatch(self, down, &p); - if(self->wayland.parent != NULL) { - self->wayland.parent->wayland.currentlyHeldWidget = self; - } + currentlyHeldWidget = self; + break; case WL_POINTER_BUTTON_STATE_RELEASED: - if(self->wayland.parent != NULL) { - if(self->wayland.parent->wayland.currentlyHeldWidget != NULL) { - MwLLDispatch(self->wayland.parent->wayland.currentlyHeldWidget, up, &p); - self->wayland.parent->wayland.currentlyHeldWidget = NULL; - } + if(currentlyHeldWidget != NULL) { + MwLLDispatch(currentlyHeldWidget, up, &p); + currentlyHeldWidget = NULL; } else { MwLLDispatch(self, up, &p); } @@ -471,7 +509,10 @@ static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 seri if(!self->wayland.always_render) { MwLLDispatch(self, draw, NULL); - self->wayland.events_pending += 1; + } + + if(!self->wayland.has_decorations) { + xdg_borderless_step(self, p, serial); } WAYLAND_EVENT_OP_END(self); @@ -530,7 +571,6 @@ static void keyboard_enter(void* data, self->wayland.keyboard_serial = serial; MwLLDispatch(self, focus_in, NULL); - self->wayland.events_pending += 1; WAYLAND_EVENT_OP_END(self); }; @@ -545,7 +585,6 @@ static void keyboard_leave(void* data, WAYLAND_EVENT_OP_START(self); MwLLDispatch(self, focus_out, NULL); - self->wayland.events_pending += 1; WAYLAND_EVENT_OP_END(self); }; @@ -627,7 +666,8 @@ static void keyboard_key(void* data, if((self->wayland.mod_state & 4) == 4) { /* clipboard paste */ if(key == 'V') { - for(int i = 0; i < arrlen(self->wayland.clipboard_devices); i++) { + int i; + for(i = 0; i < arrlen(self->wayland.clipboard_devices); i++) { wl_clipboard_read( self->wayland.clipboard_devices[i]); } @@ -648,7 +688,6 @@ static void keyboard_key(void* data, if(!self->wayland.always_render) { MwLLDispatch(self, draw, NULL); - self->wayland.events_pending += 1; } WAYLAND_EVENT_OP_END(self); @@ -698,7 +737,8 @@ static void wl_seat_capabilities(void* data, struct wl_seat* wl_seat, wl_keyboard_add_listener(self->wayland.keyboard, &keyboard_listener, data); } if(capabilities & WL_SEAT_CAPABILITY_POINTER) { - self->wayland.pointer = wl_seat_get_pointer(wl_seat); + self->wayland.pointer_seat = wl_seat; + self->wayland.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(self->wayland.pointer, &pointer_listener, data); } @@ -772,7 +812,6 @@ static wayland_protocol_t* wl_seat_setup(MwU32 name, MwLL ll) { static void wl_seat_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { free(data->listener); - // wl_seat_destroy(data->context); } /* wl_output setup function */ @@ -798,7 +837,11 @@ static void wl_compositor_interface_destroy(struct _MwLLWayland* wayland, waylan /* wl_subcompositor setup function */ static wayland_protocol_t* wl_subcompositor_setup(MwU32 name, struct _MwLLWayland* wayland) { - wayland->sublevel->subcompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1); + if(wayland->type == MWLL_WAYLAND_TOPLEVEL) { + wayland->toplevel->scompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1); + } else { + wayland->sublevel->subcompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1); + } return NULL; } @@ -821,10 +864,21 @@ static wayland_protocol_t* xdg_wm_base_setup(MwU32 name, struct _MwLLWayland* wa } static void xdg_wm_base_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { - // xdg_wm_base_destroy(data->context); free(data->listener); } +/* xdg_wm_base setup function */ +static wayland_protocol_t* wp_viewporter_setup(MwU32 name, struct _MwLLWayland* wayland) { + wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); + proto->context = wl_registry_bind(wayland->registry, name, &wp_viewporter_interface, 1); + + return proto; +} + +static void wp_viewporter_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { + // free(data->listener); +} + /* the two decoration manager constructs */ typedef struct zxdg_decoration_manager_v1_context { struct zxdg_decoration_manager_v1* manager; @@ -846,13 +900,71 @@ static wayland_protocol_t* zxdg_decoration_manager_v1_setup(MwU32 name, struct _ } static void zxdg_decoration_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { - zxdg_decoration_manager_v1_context_t* context = data->context; - - // zxdg_decoration_manager_v1_destroy(context->manager); - // zxdg_toplevel_decoration_v1_destroy(context->decoration); } -/* `xdg_toplevel.close` callback */ +/* Standard Wayland event loop. */ +static int event_loop(MwLL handle) { + struct pollfd fd; + struct timespec timeout; + struct _MwLLWayland* wayland = &handle->wayland; + int res; + + timeout.tv_nsec = 1; + timeout.tv_sec = 0; + + if(wayland->display == NULL) { + return 0; + } + fd.fd = wl_display_get_fd(wayland->display); + fd.events = POLLIN; + + /* If an error other than EAGAIN happens, we have likely been disconnected from the Wayland session */ + while(wl_display_flush(wayland->display) == -1) { + if(errno != EAGAIN) { + return MwFALSE; + } + + while(poll(&fd, 1, -1) == -1) { + if(errno != EINTR && errno != EAGAIN) { + wl_display_cancel_read(wayland->display); + + MwLLDispatch(handle, close, NULL); + return 0; + } + } + } + + wl_display_prepare_read(handle->wayland.display); + /* Condition where no events are being sent. */ + if(!poll(&fd, 1, timeout.tv_nsec)) { + wl_display_cancel_read(wayland->display); + + wl_surface_commit(wayland->framebuffer.surface); + if(wayland->type == MWLL_WAYLAND_TOPLEVEL) + wl_surface_commit(wayland->backbuffer.surface); + return 0; + } + wl_display_read_events(wayland->display); + if(wl_display_dispatch_pending(wayland->display) < 0) { + wl_display_cancel_read(wayland->display); + } + return 1; +} + +/* make the fuck sure that we're configurd */ +static void hang_until_configured(MwLL handle) { + int i = 0; + while(!handle->wayland.configured) { + if(wl_display_roundtrip(handle->wayland.display) == -1) { + printf("roundtrip failed\n"); + raise(SIGTRAP); + return; + } + event_loop(handle); + } +} + +/* `xdg_toplevel.configure` callback */ static void xdg_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel, MwI32 width, MwI32 height, @@ -877,18 +989,22 @@ static void xdg_toplevel_configure(void* data, region_invalidate(self); self->wayland.ww = width; self->wayland.wh = height; + xdg_surface_set_window_geometry(self->wayland.toplevel->xdg_surface, 0, 0, self->wayland.ww, self->wayland.wh); + backbuffer_destroy(&self->wayland); + backbuffer_setup(&self->wayland); + framebuffer_destroy(&self->wayland); framebuffer_setup(&self->wayland); region_setup(self); + if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL && !self->wayland.has_decorations) { + wp_viewport_set_destination(self->wayland.vp, self->wayland.ww - (CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT), self->wayland.wh - (CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM)); + } + MwLLDispatch(self, resize, NULL); MwLLDispatch(self, draw, NULL); - - MwLLForceRender(self); - - return; }; /* Empty function for satisfying zxdg_toplevel's requirements */ @@ -915,6 +1031,7 @@ static void xdg_surface_configure( if(self->wayland.configured) { wl_surface_commit(self->wayland.framebuffer.surface); + wl_surface_commit(self->wayland.backbuffer.surface); } self->wayland.configured = MwTRUE; @@ -922,8 +1039,8 @@ static void xdg_surface_configure( /* wl_shm setup function */ static wayland_protocol_t* wl_shm_setup(MwU32 name, struct _MwLLWayland* wayland) { - wayland->framebuffer.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1); + wayland->backbuffer.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1); wayland->cursor.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1); return NULL; @@ -984,33 +1101,74 @@ static void buffer_destroy(struct _MwLLWaylandShmBuffer* buffer) { static void framebuffer_setup(struct _MwLLWayland* wayland) { buffer_setup(&wayland->framebuffer, wayland->ww, wayland->wh); - wayland->cs = cairo_image_surface_create_for_data(wayland->framebuffer.buf, CAIRO_FORMAT_ARGB32, wayland->ww, wayland->wh, 4 * wayland->ww); - wayland->cairo = cairo_create(wayland->cs); + wayland->front_cs = cairo_image_surface_create_for_data(wayland->framebuffer.buf, CAIRO_FORMAT_ARGB32, wayland->ww, wayland->wh, 4 * wayland->ww); + wayland->front_cairo = cairo_create(wayland->front_cs); - memset(wayland->framebuffer.buf, 255, wayland->framebuffer.buf_size); + memset(wayland->framebuffer.buf, 0, wayland->framebuffer.buf_size); + hang_until_configured((MwLL)wayland); update_buffer(&wayland->framebuffer); - - wayland->events_pending += 1; }; static void framebuffer_destroy(struct _MwLLWayland* wayland) { buffer_destroy(&wayland->framebuffer); - cairo_destroy(wayland->cairo); - cairo_surface_destroy(wayland->cs); + cairo_destroy(wayland->front_cairo); + cairo_surface_destroy(wayland->front_cs); +}; + +static void backbuffer_setup(struct _MwLLWayland* wayland) { + if(wayland->type != MWLL_WAYLAND_TOPLEVEL) { + return; + } + buffer_setup(&wayland->backbuffer, wayland->ww, wayland->wh); + + wayland->back_cs = cairo_image_surface_create_for_data(wayland->backbuffer.buf, CAIRO_FORMAT_ARGB32, wayland->ww, wayland->wh, 4 * wayland->ww); + wayland->back_cairo = cairo_create(wayland->back_cs); + + memset(wayland->backbuffer.buf, 0, wayland->backbuffer.buf_size); + hang_until_configured((MwLL)wayland); + update_buffer(&wayland->backbuffer); +}; +static void backbuffer_destroy(struct _MwLLWayland* wayland) { + buffer_destroy(&wayland->backbuffer); + cairo_destroy(wayland->back_cairo); + cairo_surface_destroy(wayland->back_cs); }; static void region_invalidate(MwLL handle) { if(!handle->wayland.configured) { return; } - wl_region_subtract(handle->wayland.region, handle->wayland.x, handle->wayland.y, handle->wayland.ww, handle->wayland.wh); + wl_region_subtract(handle->wayland.region, 0, 0, handle->wayland.ww, handle->wayland.wh); } static void region_setup(MwLL handle) { + MwLL parent = handle->wayland.parent; + int width = handle->wayland.ww; + int height = handle->wayland.wh; + if(!handle->wayland.configured) { return; } - wl_region_add(handle->wayland.region, handle->wayland.x, handle->wayland.y, handle->wayland.ww, handle->wayland.wh); - wl_surface_set_input_region(handle->wayland.framebuffer.surface, handle->wayland.region); + + wl_region_add(handle->wayland.o_region, 0, 0, 1, 1); + + if(handle->wayland.type == MWLL_WAYLAND_POPUP) { + wl_region_add(handle->wayland.region, 0, 0, width + abs(handle->wayland.x), height + abs(handle->wayland.y)); + } else { + if(parent) { + if(width - handle->wayland.x > parent->wayland.ww) { + width = parent->wayland.ww - handle->wayland.x; + } + if(height - handle->wayland.y > parent->wayland.wh) { + height = parent->wayland.wh - handle->wayland.y; + } + } + wl_region_add(handle->wayland.region, 0, 0, width, height); + } + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { + wl_surface_set_opaque_region(handle->wayland.backbuffer.surface, handle->wayland.region); + wl_surface_set_input_region(handle->wayland.backbuffer.surface, handle->wayland.region); + } wl_surface_set_opaque_region(handle->wayland.framebuffer.surface, handle->wayland.region); + wl_surface_set_input_region(handle->wayland.framebuffer.surface, handle->wayland.region); } static wayland_protocol_t* xdg_toplevel_icon_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland) { @@ -1024,60 +1182,6 @@ static wayland_protocol_t* xdg_toplevel_icon_manager_v1_setup(MwU32 name, struct static void xdg_toplevel_icon_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { free(data->listener); - // xdg_toplevel_icon_manager_v1_destroy(data->context); -} - -/* Standard Wayland event loop. */ -static int event_loop(MwLL handle) { - struct pollfd fd; - int timeout = 1; - struct _MwLLWayland* wayland = &handle->wayland; - - if(wayland->display == NULL) { - return 0; - } - fd.fd = wl_display_get_fd(wayland->display); - fd.events = POLLIN; - - while(wl_display_prepare_read(handle->wayland.display) != 0) { - if(wl_display_dispatch_pending(handle->wayland.display) > 0) { - return 0; - } - } - - /* If an error other than EAGAIN happens, we have likely been disconnected from the Wayland session */ - while(wl_display_flush(wayland->display) == -1) { - if(errno != EAGAIN) { - return MwFALSE; - } - - while(poll(&fd, 1, -1) == -1) { - if(errno != EINTR && errno != EAGAIN) { - wl_display_cancel_read(wayland->display); - - MwLLDispatch(handle, close, NULL); - return 0; - } - } - } - - /* Condition where no events are being sent. */ - if(!poll(&fd, 1, timeout)) { - wl_display_cancel_read(handle->wayland.display); - /* In this case, we need to commit the surface for any animations, etc. */ - wl_surface_commit(handle->wayland.framebuffer.surface); - return 0; - } - - if(fd.revents & POLLIN) { - wl_display_read_events(wayland->display); - if(wl_display_dispatch_pending(wayland->display) > 0) { - } - } else { - wl_display_cancel_read(handle->wayland.display); - } - - return 1; } /* Function for setting up the callbacks/structs that will be registered upon the relevant interfaces being found. */ @@ -1104,8 +1208,10 @@ static void setup_callbacks(struct _MwLLWayland* wayland) { WL_INTERFACE(zwp_primary_selection_device_manager_v1); if(wayland->type == MWLL_WAYLAND_TOPLEVEL) { WL_INTERFACE(xdg_wm_base); + WL_INTERFACE(wp_viewporter); WL_INTERFACE(zxdg_decoration_manager_v1); WL_INTERFACE(xdg_toplevel_icon_manager_v1); + WL_INTERFACE(wl_subcompositor); } else if(wayland->type == MWLL_WAYLAND_POPUP) { WL_INTERFACE(xdg_wm_base); } else { @@ -1120,6 +1226,8 @@ static void setup_toplevel(MwLL r, int x, int y) { r->wayland.type = MWLL_WAYLAND_TOPLEVEL; r->wayland.toplevel = malloc(sizeof(struct _MwLLWaylandTopLevel)); + r->wayland.x = x; + r->wayland.y = y; setup_callbacks(&r->wayland); @@ -1131,8 +1239,6 @@ static void setup_toplevel(MwLL r, int x, int y) { return; } - r->wayland.framebuffer.surface = NULL; - /* Do a roundtrip to ensure all interfaces are setup. */ r->wayland.registry = wl_display_get_registry(r->wayland.display); wl_registry_add_listener(r->wayland.registry, &r->wayland.registry_listener, r); @@ -1146,9 +1252,12 @@ static void setup_toplevel(MwLL r, int x, int y) { /* Create a wl_surface, a xdg_surface and a xdg_toplevel */ r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor); + r->wayland.backbuffer.surface = wl_compositor_create_surface(r->wayland.compositor); + r->wayland.toplevel->ssurface = wl_subcompositor_get_subsurface(r->wayland.toplevel->scompositor, r->wayland.framebuffer.surface, r->wayland.backbuffer.surface); + wl_subsurface_set_desync(r->wayland.toplevel->ssurface); r->wayland.toplevel->xdg_surface = - xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.framebuffer.surface); + xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.backbuffer.surface); r->wayland.toplevel->xdg_top_level = xdg_surface_get_toplevel(r->wayland.toplevel->xdg_surface); /* setup mandatory listeners */ @@ -1165,6 +1274,7 @@ static void setup_toplevel(MwLL r, int x, int y) { xdg_toplevel_set_app_id(r->wayland.toplevel->xdg_top_level, "MilskoWaylandApp"); /* Perform the initial commit and wait for the first configure event */ + wl_surface_commit(r->wayland.backbuffer.surface); wl_surface_commit(r->wayland.framebuffer.surface); while(!r->wayland.configured) { event_loop(r); @@ -1179,8 +1289,17 @@ static void setup_toplevel(MwLL r, int x, int y) { zxdg_toplevel_decoration_v1_set_mode( dec->decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + + r->wayland.has_decorations = MwTRUE; } else { - printf("zxdg null\n"); + /* otherwise set up viewporter */ + struct wp_viewporter* wp = WAYLAND_GET_INTERFACE(r->wayland, wp_viewporter)->context; + r->wayland.vp = wp_viewporter_get_viewport(wp, r->wayland.framebuffer.surface); + wp_viewport_set_source(r->wayland.vp, r->wayland.x, r->wayland.y, r->wayland.ww, r->wayland.wh); + wp_viewport_set_destination(r->wayland.vp, r->wayland.ww - (CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT), r->wayland.wh - (CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM)); + r->wayland.has_decorations = MwFALSE; + + wl_subsurface_set_position(r->wayland.toplevel->ssurface, CSD_BORDER_FRAME_LEFT, CSD_BORDER_FRAME_TOP); } } @@ -1212,6 +1331,8 @@ static void destroy_toplevel(MwLL r) { wl_registry_destroy(r->wayland.registry); wl_display_disconnect(r->wayland.display); + + r->wayland.configured = MwFALSE; } /* Sublevel setup function */ @@ -1248,18 +1369,26 @@ static void setup_sublevel(MwLL parent, MwLL r, int x, int y) { r->wayland.sublevel->subsurface = wl_subcompositor_get_subsurface(r->wayland.sublevel->subcompositor, r->wayland.framebuffer.surface, parent_surface); + wl_subsurface_set_desync(r->wayland.sublevel->subsurface); wl_subsurface_set_position(r->wayland.sublevel->subsurface, x, y); + if(parent) { + wl_subsurface_place_above(r->wayland.sublevel->subsurface, parent->wayland.framebuffer.surface); + } + r->wayland.configured = MwTRUE; } /* Sublevel setup function */ static void destroy_sublevel(MwLL r) { + backbuffer_destroy(&r->wayland); + framebuffer_destroy(&r->wayland); + wl_subsurface_destroy(r->wayland.sublevel->subsurface); - wl_surface_destroy(r->wayland.framebuffer.surface); - free(r->wayland.sublevel); + + r->wayland.configured = MwFALSE; } static void popup_configure(void* data, @@ -1298,6 +1427,10 @@ static void setup_popup(MwLL r, int x, int y) { while(topmost_parent->wayland.type != MWLL_WAYLAND_TOPLEVEL) { topmost_parent = topmost_parent->wayland.parent; } + if(!topmost_parent->wayland.has_decorations) { + r->wayland.x += ceil((float)CSD_BORDER_FRAME_LEFT / 2.); + r->wayland.y += ceil((float)CSD_BORDER_FRAME_TOP / 2.); + } r->wayland.popup = malloc(sizeof(struct _MwLLWaylandPopup)); memset(r->wayland.popup, 0, sizeof(struct _MwLLWaylandPopup)); @@ -1319,10 +1452,9 @@ static void setup_popup(MwLL r, int x, int y) { r->wayland.popup->xdg_positioner = xdg_wm_base_create_positioner(r->wayland.popup->xdg_wm_base); xdg_positioner_set_size(r->wayland.popup->xdg_positioner, r->wayland.ww, r->wayland.wh); - xdg_positioner_set_anchor(r->wayland.popup->xdg_positioner, XDG_POSITIONER_ANCHOR_NONE); xdg_positioner_set_anchor_rect( - r->wayland.popup->xdg_positioner, 0, 0, 1, 1); - xdg_positioner_set_offset(r->wayland.popup->xdg_positioner, x, y); + r->wayland.popup->xdg_positioner, + r->wayland.x, r->wayland.y, r->wayland.ww, r->wayland.wh); xdg_surface = topmost_parent->wayland.toplevel->xdg_surface; @@ -1345,6 +1477,10 @@ static void setup_popup(MwLL r, int x, int y) { /* Perform the initial commit and wait for the first configure event */ wl_surface_commit(r->wayland.framebuffer.surface); event_loop(r); + + // framebuffer_destroy(&r->wayland); + framebuffer_setup(&r->wayland); + backbuffer_setup(&r->wayland); } /* Popup destroy function */ @@ -1358,6 +1494,34 @@ static void destroy_popup(MwLL r) { wl_surface_destroy(r->wayland.framebuffer.surface); wl_registry_destroy(r->wayland.registry); + + r->wayland.configured = MwFALSE; +} + +static void clip(MwLL handle) { + MwLL parent = handle->wayland.parent; + MwLL topmost = parent; + int left_offset = 0; + int top_offset = 0; + + if(parent && handle->wayland.type == MWLL_WAYLAND_SUBLEVEL) { + cairo_reset_clip(handle->wayland.front_cairo); + cairo_rectangle(handle->wayland.front_cairo, 0, 0, (parent->wayland.ww + handle->wayland.x), (parent->wayland.wh + handle->wayland.y)); + cairo_clip(handle->wayland.front_cairo); + } + if(topmost && handle->wayland.type == MWLL_WAYLAND_SUBLEVEL) { + cairo_reset_clip(handle->wayland.front_cairo); + while(topmost->wayland.parent) topmost = topmost->wayland.parent; + left_offset = topmost->wayland.has_decorations ? 0 : (CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT); + top_offset = topmost->wayland.has_decorations ? 0 : (CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM); + cairo_rectangle(handle->wayland.front_cairo, 0, 0, (topmost->wayland.ww + handle->wayland.x) - left_offset, (topmost->wayland.wh + handle->wayland.y) - top_offset); + cairo_clip(handle->wayland.front_cairo); + } +} + +static void wl_logger(const char* fmt, va_list args) { + vprintf(fmt, args); + raise(SIGTRAP); } static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { @@ -1366,6 +1530,8 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { memset(r, 0, sizeof(*r)); MwLLCreateCommon(r); + wl_log_set_handler_client(wl_logger); + /* Wayland does not report global coordinates ever. Compositors are not even expected to have knowledge of this. */ r->common.coordinate_type = MwCoordinatesLocal; @@ -1395,8 +1561,10 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { } framebuffer_setup(&r->wayland); + backbuffer_setup(&r->wayland); - r->wayland.region = wl_compositor_create_region(r->wayland.compositor); + r->wayland.region = wl_compositor_create_region(r->wayland.compositor); + r->wayland.o_region = wl_compositor_create_region(r->wayland.compositor); region_setup(r); MwLLForceRender(r); @@ -1412,7 +1580,7 @@ static void MwLLDestroyImpl(MwLL handle) { struct timeval tv; struct timespec t = { .tv_sec = 0, - .tv_nsec = 100, + .tv_nsec = 0, }; int select_ret; @@ -1435,7 +1603,7 @@ static void MwLLDestroyImpl(MwLL handle) { pthread_mutex_unlock(&handle->wayland.eventsMutex); pthread_mutex_destroy(&handle->wayland.eventsMutex); - framebuffer_destroy(&handle->wayland); + // framebuffer_destroy(&handle->wayland); buffer_destroy(&handle->wayland.cursor); wl_region_destroy(handle->wayland.region); @@ -1485,14 +1653,9 @@ static void MwLLSetXYImpl(MwLL handle, int x, int y) { if(handle->wayland.type == MWLL_WAYLAND_SUBLEVEL) { wl_subsurface_set_position(handle->wayland.sublevel->subsurface, x, y); } - if(handle->wayland.type == MWLL_WAYLAND_POPUP) { - destroy_popup(handle); - setup_popup(handle, x, y); - } region_setup(handle); MwLLDispatch(handle, draw, NULL); - handle->wayland.events_pending += 1; } static void MwLLSetWHImpl(MwLL handle, int w, int h) { @@ -1504,65 +1667,177 @@ static void MwLLSetWHImpl(MwLL handle, int w, int h) { handle->wayland.wh = 10; goto refresh; } + if(handle->wayland.parent) { + if(handle->wayland.parent->wayland.type == MWLL_WAYLAND_TOPLEVEL && !handle->wayland.parent->wayland.has_decorations) { + if(w >= handle->wayland.parent->wayland.ww - (CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT)) + w = handle->wayland.parent->wayland.ww - (CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT); + if(h >= handle->wayland.parent->wayland.wh - (CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM)) + h = handle->wayland.parent->wayland.wh - (CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM); + } + } handle->wayland.ww = w; handle->wayland.wh = h; + // if(handle->wayland.wh >= (CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM)) { + // handle->wayland.wh -= (CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM); + // } + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL && handle->wayland.configured) { xdg_surface_set_window_geometry(handle->wayland.toplevel->xdg_surface, 0, 0, handle->wayland.ww, handle->wayland.wh); } + if(handle->wayland.type == MWLL_WAYLAND_POPUP) { + destroy_popup(handle); + wl_flush(handle); + setup_popup(handle, handle->wayland.x, handle->wayland.y); + } + refresh: region_setup(handle); framebuffer_destroy(&handle->wayland); framebuffer_setup(&handle->wayland); + backbuffer_destroy(&handle->wayland); + backbuffer_setup(&handle->wayland); MwLLDispatch(handle, draw, NULL); } +static void MwLLBeginDrawImpl(MwLL handle) { + if(handle->wayland.type != MWLL_WAYLAND_TOPLEVEL) { + handle->wayland.selected_cairo = handle->wayland.front_cairo; + return; + } + if(!handle->wayland.has_decorations) { + int x, y; + cairo_set_line_width(handle->wayland.back_cairo, 4.0); + for(x = 0; x < handle->wayland.ww; x++) { + float placeholder = (float)x / (float)handle->wayland.ww; + cairo_set_source_rgb(handle->wayland.back_cairo, placeholder, 0.25, 0.25); + + cairo_move_to(handle->wayland.back_cairo, handle->wayland.ww - x, 0); + cairo_line_to(handle->wayland.back_cairo, handle->wayland.ww - x, CSD_BORDER_FRAME_TOP); + cairo_stroke(handle->wayland.back_cairo); + + cairo_move_to(handle->wayland.back_cairo, x, CSD_BORDER_FRAME_TOP); + cairo_line_to(handle->wayland.back_cairo, x, handle->wayland.wh); + cairo_stroke(handle->wayland.back_cairo); + } + cairo_set_source_rgba(handle->wayland.back_cairo, 1, 1, 1, 0.5); + cairo_move_to(handle->wayland.back_cairo, 0, 0); + cairo_line_to(handle->wayland.back_cairo, 0, handle->wayland.wh); + cairo_stroke(handle->wayland.back_cairo); + cairo_move_to(handle->wayland.back_cairo, 0, 0); + cairo_line_to(handle->wayland.back_cairo, handle->wayland.ww, 0); + cairo_stroke(handle->wayland.back_cairo); + + cairo_set_source_rgba(handle->wayland.back_cairo, 0, 0, 0, 0.5); + cairo_move_to(handle->wayland.back_cairo, 0, handle->wayland.wh); + cairo_line_to(handle->wayland.back_cairo, handle->wayland.ww, handle->wayland.wh); + cairo_stroke(handle->wayland.back_cairo); + cairo_move_to(handle->wayland.back_cairo, handle->wayland.ww, 0); + cairo_line_to(handle->wayland.back_cairo, handle->wayland.ww, handle->wayland.wh); + cairo_stroke(handle->wayland.back_cairo); + + if(strlen(handle->wayland.title) != 0) { + int y, x; + int i = 0, sx = 0, sy = 0; + int tw, th; + unsigned char* px; + MwRect r; + MwLLPixmap p; + tw = strlen(handle->wayland.title) * 7; + th = 14; + px = malloc(tw * th * 4); + assert(px); + + memset(px, 0, tw * th * 4); + + handle->wayland.selected_cairo = handle->wayland.back_cairo; + + while(handle->wayland.title[i]) { + int out; + i += MwUTF8ToUTF32(handle->wayland.title + i, &out); + + if(out > 0xff) { + out = 0; + } + + for(y = 0; y < 14; y++) { + for(x = 0; x < 7; x++) { + unsigned char* ppx = &px[((sy + y) * tw + sx + x) * 4]; + int col = MwFontData[out].data[y] & (1 << ((7 - 1) - x)) ? 255 : 0; + ppx[0] = col; + ppx[1] = col; + ppx[2] = col; + ppx[3] = col; + } + } + sx += 7; + } + + p = MwLLCreatePixmap(handle, px, tw, th); + r.x = 5; + r.y = 5; + r.width = tw; + r.height = th; + + MwLLDrawPixmap(handle, &r, p); + MwLLDestroyPixmap(p); + free(px); + } + update_buffer(&handle->wayland.backbuffer); + + wl_surface_commit(handle->wayland.backbuffer.surface); + } + handle->wayland.selected_cairo = handle->wayland.front_cairo; +} + +static void MwLLEndDrawImpl(MwLL handle) { + if(handle->wayland.configured) { + update_buffer(&handle->wayland.framebuffer); + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { + } + } +} + static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count, MwLLColor color) { int i; - cairo_set_source_rgb(handle->wayland.cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); - cairo_new_path(handle->wayland.cairo); + clip(handle); + + cairo_set_source_rgb(handle->wayland.front_cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); + cairo_new_path(handle->wayland.front_cairo); for(i = 0; i < points_count; i++) { if(i == 0) { - cairo_move_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_move_to(handle->wayland.front_cairo, points[i].x, points[i].y); } else { - cairo_line_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_line_to(handle->wayland.front_cairo, points[i].x, points[i].y); } } - cairo_close_path(handle->wayland.cairo); - cairo_fill(handle->wayland.cairo); + cairo_close_path(handle->wayland.front_cairo); - handle->wayland.events_pending += 1; + cairo_fill(handle->wayland.front_cairo); + + handle->wayland.events_pending = 1; } static void MwLLLineImpl(MwLL handle, MwPoint* points, MwLLColor color) { int i; - cairo_set_line_cap(handle->wayland.cairo, CAIRO_LINE_CAP_SQUARE); - cairo_set_source_rgb(handle->wayland.cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); - cairo_new_path(handle->wayland.cairo); + cairo_set_line_cap(handle->wayland.front_cairo, CAIRO_LINE_CAP_SQUARE); + cairo_set_source_rgb(handle->wayland.front_cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); + cairo_new_path(handle->wayland.front_cairo); for(i = 0; i < 2; i++) { if(i == 0) { - cairo_move_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_move_to(handle->wayland.front_cairo, points[i].x, points[i].y); } else { - cairo_line_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_line_to(handle->wayland.front_cairo, points[i].x, points[i].y); } } - cairo_close_path(handle->wayland.cairo); - cairo_stroke(handle->wayland.cairo); + cairo_close_path(handle->wayland.front_cairo); + cairo_stroke(handle->wayland.front_cairo); - handle->wayland.events_pending += 1; -} - -static void MwLLBeginDrawImpl(MwLL handle) { -} - -static void MwLLEndDrawImpl(MwLL handle) { - update_buffer(&handle->wayland.framebuffer); - - handle->wayland.events_pending += 1; + handle->wayland.events_pending = 1; } static MwLLColor MwLLAllocColorImpl(MwLL handle, int r, int g, int b) { @@ -1586,43 +1861,66 @@ static void MwLLFreeColorImpl(MwLLColor color) { } static int MwLLPendingImpl(MwLL handle) { - MwBool pending = MwFALSE; - struct timespec timeout; - int i; + struct timespec timeout = { + .tv_nsec = 100, + .tv_sec = 0, + }; + struct pollfd fd = { + .fd = wl_display_get_fd(handle->wayland.display), + .events = POLLOUT, + }; + int pending = 0; + + if(!handle->wayland.always_render) wl_display_prepare_read(handle->wayland.display); + if(!poll(&fd, 1, timeout.tv_nsec)) { + wl_display_cancel_read(handle->wayland.display); + } else { + if(!handle->wayland.always_render) wl_display_read_events(handle->wayland.display); + if((pending = wl_display_dispatch_pending(handle->wayland.display)) < 0) { + wl_display_cancel_read(handle->wayland.display); + } + } if(handle->wayland.always_render) { - return event_loop(handle); + handle->wayland.did_event_loop_early = MwTRUE; + event_loop(handle); + return pending; } - timeout.tv_nsec = 10; - timeout.tv_sec = 0; - - if(handle->wayland.force_render) { - return 1; - } - if(handle->wayland.events_pending) { - return 1; + wl_surface_commit(handle->wayland.framebuffer.surface); + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { + wl_surface_commit(handle->wayland.backbuffer.surface); } - return wl_display_dispatch_timeout(handle->wayland.display, &timeout); + return handle->wayland.force_render || handle->wayland.events_pending || pending; } static void MwLLNextEventImpl(MwLL handle) { if(!handle->wayland.always_render) { - event_loop(handle); + if(handle->wayland.did_event_loop_early) { + handle->wayland.did_event_loop_early = MwFALSE; + } else { + event_loop(handle); + } + } + + if(handle->wayland.force_render) { + if(!handle->wayland.events_pending) MwLLDispatch(handle, draw, NULL); + if(handle->wayland.configured) update_buffer(&handle->wayland.framebuffer); + handle->wayland.force_render = 0; } if(handle->wayland.events_pending) { handle->wayland.events_pending = 0; } - if(handle->wayland.force_render) { - handle->wayland.force_render = 0; - } } static void MwLLSetTitleImpl(MwLL handle, const char* title) { if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { xdg_toplevel_set_title(handle->wayland.toplevel->xdg_top_level, title); } + if(!handle->wayland.has_decorations) { + strncpy(handle->wayland.title, title, 255); + } } static MwLLPixmap MwLLCreatePixmapImpl(MwLL handle, unsigned char* data, int width, int height) { @@ -1669,21 +1967,29 @@ static void MwLLDestroyPixmapImpl(MwLLPixmap pixmap) { static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { cairo_t* c; cairo_surface_t* cs; + cairo_t* selected_cairo = handle->wayland.selected_cairo ? handle->wayland.selected_cairo : handle->wayland.front_cairo; cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect->width, rect->height); c = cairo_create(cs); + clip(handle); + cairo_scale(c, (double)rect->width / pixmap->common.width, (double)rect->height / pixmap->common.height); + cairo_set_source_surface(c, pixmap->wayland.cs, 0, 0); cairo_pattern_set_filter(cairo_get_source(c), CAIRO_FILTER_NEAREST); + cairo_paint(c); - cairo_set_source_rgb(handle->wayland.cairo, 1, 1, 1); - cairo_set_source_surface(handle->wayland.cairo, cs, rect->x, rect->y); - cairo_paint(handle->wayland.cairo); + cairo_set_source_surface(selected_cairo, cs, rect->x, rect->y); + cairo_paint(selected_cairo); cairo_destroy(c); cairo_surface_destroy(cs); + + MwLLForceRender(handle); + update_buffer(&handle->wayland.framebuffer); + wl_surface_damage(handle->wayland.framebuffer.surface, 0, 0, handle->wayland.ww, handle->wayland.wh); } static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) { if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { @@ -1715,7 +2021,7 @@ static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) { handle->wayland.icon->buf[i + 3] = 255; } - update_buffer(handle->wayland.icon); + if(handle->wayland.configured) update_buffer(handle->wayland.icon); xdg_toplevel_icon_v1_add_buffer(icon, handle->wayland.icon->shm_buffer, 1); @@ -1728,12 +2034,9 @@ static void MwLLForceRenderImpl(MwLL handle) { wl_surface_damage(handle->wayland.framebuffer.surface, 0, 0, handle->wayland.ww, handle->wayland.wh); handle->wayland.force_render = MwTRUE; - - /* - if(handle->wayland.egl_setup) { - timed_redraw_by_epoch(handle, 25); + if(handle->wayland.parent) { + handle->wayland.parent->wayland.force_render = MwTRUE; } - */ } static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) { @@ -1787,29 +2090,20 @@ static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) { } static void MwLLDetachImpl(MwLL handle, MwPoint* point) { - switch(handle->wayland.type) { - case MWLL_WAYLAND_UNKNOWN: - case MWLL_WAYLAND_TOPLEVEL: - destroy_toplevel(handle); - break; - case MWLL_WAYLAND_SUBLEVEL: - destroy_sublevel(handle); - break; - case MWLL_WAYLAND_POPUP: - return; - } - - switch(handle->wayland.type_to_be) { - case MWLL_WAYLAND_POPUP: - setup_popup(handle, point->x, point->y); - return; - default: - setup_toplevel(handle, point->x, point->y); - break; - } + handle->wayland.detatching = MwTRUE; + handle->wayland.detach_point = *point; } static void MwLLShowImpl(MwLL handle, int show) { + switch(handle->wayland.type) { + case MWLL_WAYLAND_UNKNOWN: + case MWLL_WAYLAND_TOPLEVEL: + break; + case MWLL_WAYLAND_SUBLEVEL: + break; + case MWLL_WAYLAND_POPUP: + break; + } } static void MwLLMakePopupImpl(MwLL handle, MwLL parent) { @@ -1832,9 +2126,6 @@ static void MwLLMakeBorderlessImpl(MwLL handle, int toggle) { zxdg_toplevel_decoration_v1_set_mode( dec->decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); - } else { - /* TODO: hide our custom window decorations when we have them. */ - printf("zxdg null\n"); } } } @@ -1854,12 +2145,14 @@ static void MwLLSetClipboardImpl(MwLL handle, const char* text) { strcpy(handle->wayland.clipboard_buffer, text); if(handle->wayland.supports_zwp) { - for(int i = 0; i < arrlen(handle->wayland.clipboard_devices); i++) { + int i; + for(i = 0; i < arrlen(handle->wayland.clipboard_devices); i++) { wl_clipboard_device_context_t* device = handle->wayland.clipboard_devices[i]; zwp_primary_selection_device_v1_set_selection(device->device.zwp, handle->wayland.clipboard_source.zwp, handle->wayland.keyboard_serial); } } else { - for(int i = 0; i < arrlen(handle->wayland.clipboard_devices); i++) { + int i; + for(i = 0; i < arrlen(handle->wayland.clipboard_devices); i++) { wl_clipboard_device_context_t* device = handle->wayland.clipboard_devices[i]; wl_data_device_set_selection(device->device.wl, handle->wayland.clipboard_source.wl, handle->wayland.keyboard_serial); } @@ -1891,6 +2184,32 @@ static void MwLLBeginStateChangeImpl(MwLL handle) { } static void MwLLEndStateChangeImpl(MwLL handle) { + if(handle->wayland.detatching) { + switch(handle->wayland.type) { + case MWLL_WAYLAND_UNKNOWN: + case MWLL_WAYLAND_TOPLEVEL: + destroy_toplevel(handle); + break; + case MWLL_WAYLAND_SUBLEVEL: + destroy_sublevel(handle); + break; + case MWLL_WAYLAND_POPUP: + return; + } + + wl_flush(handle); + + switch(handle->wayland.type_to_be) { + case MWLL_WAYLAND_POPUP: + setup_popup(handle, handle->wayland.detach_point.x, handle->wayland.detach_point.y); + break; + default: + setup_toplevel(handle, handle->wayland.detach_point.x, handle->wayland.detach_point.y); + break; + } + handle->wayland.detatching = MwFALSE; + } + MwLLShow(handle, 1); } diff --git a/src/core.c b/src/core.c index 78d2a946..39cae924 100644 --- a/src/core.c +++ b/src/core.c @@ -49,6 +49,7 @@ static void llresizehandler(MwLL handle, void* data) { MwDispatchUserHandler(h, MwNresizeHandler, NULL); for(i = 0; i < arrlen(h->children); i++) { MwDispatch(h->children[i], parent_resize); + MwDispatch(h->children[i], draw); } MwDispatch(h, resize); } @@ -127,6 +128,17 @@ static void llclipboardhandler(MwLL handle, void* data) { */ #define IsFirstVisible(handle) ((handle)->widget_class != NULL && ((handle)->parent == NULL || (handle)->parent->widget_class == NULL)) +static void lldarkthemehandler(MwLL handle, void* data) { + MwWidget h = (MwWidget)handle->common.user; + int* ptr = data; + + if(IsFirstVisible(h)) { + MwSetDarkTheme(h, *ptr); + + MwDispatchUserHandler(h, MwNdarkThemeHandler, data); + } +} + MwWidget MwCreateWidget(MwClass widget_class, const char* name, MwWidget parent, int x, int y, unsigned int width, unsigned int height) { MwWidget h = malloc(sizeof(*h)); @@ -171,6 +183,7 @@ MwWidget MwCreateWidget(MwClass widget_class, const char* name, MwWidget parent, h->lowlevel->common.handler->focus_in = llfocusinhandler; h->lowlevel->common.handler->focus_out = llfocusouthandler; h->lowlevel->common.handler->clipboard = llclipboardhandler; + h->lowlevel->common.handler->dark_theme = lldarkthemehandler; } if(parent != NULL) arrput(parent->children, h); @@ -535,6 +548,7 @@ const char* MwGetText(MwWidget handle, const char* key) { return shget(handle->text, key); } +#if defined(USE_STB_TRUETYPE) || defined(USE_FREETYPE2) static void* inherit_void(MwWidget handle, const char* key) { void* v; @@ -543,6 +557,7 @@ static void* inherit_void(MwWidget handle, const char* key) { } return NULL; } +#endif void* MwGetVoid(MwWidget handle, const char* key) { void* v = shget(handle->data, key); @@ -739,7 +754,7 @@ static void force_render_all(MwWidget handle) { if(handle->lowlevel != NULL) MwForceRender(handle); } -void MwToggleDarkTheme(MwWidget handle, int toggle) { +void MwSetDarkTheme(MwWidget handle, int toggle) { int old = handle->dark_theme; if(old != toggle) { handle->dark_theme = toggle; @@ -770,6 +785,8 @@ int MwLibraryInit(void) { NULL}; int i; + MwFLSetup(); + for(i = 0; calls[i] != NULL; i++) { if(calls[i]() == 0) return 0; } @@ -798,6 +815,8 @@ void MwReparent(MwWidget handle, MwWidget new_parent) { arrput(new_parent->children, handle); MwDispatch(handle->parent, children_update); + + MwForceRender(handle); } MwClass MwGetClass(MwWidget handle) { diff --git a/src/text.c b/src/text.c deleted file mode 100644 index c0e00c6d..00000000 --- a/src/text.c +++ /dev/null @@ -1,384 +0,0 @@ -#include - -#if defined(USE_FREETYPE2) -#include -#include FT_FREETYPE_H - -typedef struct ttf { - FT_Library library; - FT_Face face; - void* data; -} ttf_t; - -#define TTF -#elif defined(USE_STB_TRUETYPE) -#include "../external/stb_truetype.h" - -typedef struct ttf { - stbtt_fontinfo font; - void* data; - float scale; - int ascent; - int descent; -} ttf_t; - -#define TTF -#endif - -#define FontWidth 7 -#define FontHeight 14 - -static void bitmap_MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { - int i = 0, x, y, sx, sy; - int tw, th; - unsigned char* px; - MwRect r; - MwLLPixmap p; - - if(strlen(text) == 0) text = " "; - tw = MwTextWidth(handle, text); - th = MwTextHeight(handle, text); - px = malloc(tw * th * 4); - - memset(px, 0, tw * th * 4); - - sx = 0; - sy = 0; - - while(text[i] != 0) { - int out; - i += MwUTF8ToUTF32(text + i, &out); - - if(out > 0xff) { - out = 0; - } - - if(out == '\n') { - sx = 0; - sy += FontHeight; - } else { - for(y = 0; y < FontHeight; y++) { - for(x = 0; x < FontWidth; x++) { - unsigned char* ppx = &px[((sy + y) * tw + sx + x) * 4]; - if((bold ? MwBoldFontData : MwFontData)[out].data[y] & (1 << ((FontWidth - 1) - x))) { - ppx[0] = color->common.red; - ppx[1] = color->common.green; - ppx[2] = color->common.blue; - ppx[3] = 255; - } else { - ppx[0] = 0; - ppx[1] = 0; - ppx[2] = 0; - ppx[3] = 0; - } - } - } - sx += FontWidth; - } - } - - p = MwLoadRaw(handle, px, tw, th); - r.x = point->x; - r.y = point->y - th / 2; - r.width = tw; - r.height = th; - - if(align == MwALIGNMENT_CENTER) { - r.x -= tw / 2; - } else if(align == MwALIGNMENT_END) { - r.x -= tw; - } - - MwLLDrawPixmap(handle->lowlevel, &r, p); - MwLLDestroyPixmap(p); - free(px); -} - -#if defined(USE_STB_TRUETYPE) -static int ttf_MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { - ttf_t* ttf = MwGetVoid(handle, bold ? MwNboldFont : MwNfont); - unsigned char* px; - int tw, th; - MwRect r; - MwLLPixmap p; - int ax, lsb; - int x = 0, y = 0; - if(ttf == NULL) return 1; - - tw = MwTextWidth(handle, text); - th = MwTextHeight(handle, text); - px = malloc(tw * th * 4); - - memset(px, 0, tw * th * 4); - while(text[0] != 0) { - int c; - int x0, y0, x1, y1, cx, cy; - int ow, oh; - unsigned char* out; - - text += MwUTF8ToUTF32(text, &c); - if(c == '\n') { - x = 0; - y += (ttf->ascent - ttf->descent) * ttf->scale; - continue; - } - - stbtt_GetCodepointHMetrics(&ttf->font, c, &ax, &lsb); - - stbtt_GetCodepointBitmapBox(&ttf->font, c, ttf->scale, ttf->scale, &x0, &y0, &x1, &y1); - ow = x1 - x0; - oh = y1 - y0; - out = malloc(ow * oh); - stbtt_MakeCodepointBitmap(&ttf->font, out, ow, oh, ow, ttf->scale, ttf->scale, c); - - for(cy = 0; cy < oh; cy++) { - for(cx = 0; cx < ow; cx++) { - int ox = x + (lsb * ttf->scale) + cx; - int oy = y + (ttf->ascent * ttf->scale) + y0 + cy; - unsigned char* opx = &px[(oy * tw + ox) * 4]; - - opx[0] = color->common.red; - opx[1] = color->common.green; - opx[2] = color->common.blue; - opx[3] = out[cy * ow + cx]; - } - } - - x += ax * ttf->scale; - - free(out); - } - - p = MwLoadRaw(handle, px, tw, th); - r.x = point->x; - r.y = point->y - th / 2; - r.width = tw; - r.height = th; - - if(align == MwALIGNMENT_CENTER) { - r.x -= tw / 2; - } else if(align == MwALIGNMENT_END) { - r.x -= tw; - } - - MwLLDrawPixmap(handle->lowlevel, &r, p); - MwLLDestroyPixmap(p); - free(px); - - return 0; -} - -static int ttf_MwTextWidth(MwWidget handle, const char* text) { - ttf_t* ttf = MwGetVoid(handle, MwNfont); - int ax, lsb; - int tw = 0; - if(ttf == NULL) return -1; - - while(text[0] != 0) { - int c; - text += MwUTF8ToUTF32(text, &c); - - stbtt_GetCodepointHMetrics(&ttf->font, c, &ax, &lsb); - - tw += ax * ttf->scale; - } - - return tw; -} - -static int ttf_MwTextHeight(MwWidget handle, int count) { - ttf_t* ttf = MwGetVoid(handle, MwNfont); - if(ttf == NULL) return -1; - - return (ttf->ascent - ttf->descent) * ttf->scale * count; -} -#elif defined(USE_FREETYPE2) -static int ttf_MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { - ttf_t* ttf = MwGetVoid(handle, bold ? MwNboldFont : MwNfont); - int tw, th; - unsigned char* px; - MwLLPixmap p; - MwRect r; - int x = 0, y = 0; - if(ttf == NULL) return 1; - - tw = MwTextWidth(handle, text); - th = MwTextHeight(handle, text); - px = malloc(tw * th * 4); - - memset(px, 0, tw * th * 4); - while(text[0] != 0) { - int c; - FT_Bitmap* bmp; - int cy, cx; - int l = MwUTF8ToUTF32(text, &c); - if(l <= 0) break; - text += l; - - if(c == '\n') { - x = 0; - y += ttf->face->height * 14 / ttf->face->units_per_EM; - continue; - } - - FT_Load_Char(ttf->face, c, FT_LOAD_RENDER); - bmp = &ttf->face->glyph->bitmap; - - for(cy = 0; cy < bmp->rows; cy++) { - for(cx = 0; cx < bmp->width; cx++) { - int ox = x + cx + ttf->face->glyph->bitmap_left; - int oy = y + (ttf->face->height * 14 / ttf->face->units_per_EM) - ttf->face->glyph->bitmap_top + cy + (ttf->face->descender * 14 / ttf->face->units_per_EM); - unsigned char* opx = &px[(oy * tw + ox) * 4]; - - opx[0] = color->common.red; - opx[1] = color->common.green; - opx[2] = color->common.blue; - opx[3] = bmp->buffer[cy * bmp->pitch + cx]; - } - } - - x += ttf->face->glyph->metrics.horiAdvance / 64; - } - - p = MwLoadRaw(handle, px, tw, th); - r.x = point->x; - r.y = point->y - th / 2; - r.width = tw; - r.height = th; - - if(align == MwALIGNMENT_CENTER) { - r.x -= tw / 2; - } else if(align == MwALIGNMENT_END) { - r.x -= tw; - } - - MwLLDrawPixmap(handle->lowlevel, &r, p); - MwLLDestroyPixmap(p); - free(px); - - return 0; -} - -static int ttf_MwTextWidth(MwWidget handle, const char* text) { - ttf_t* ttf = MwGetVoid(handle, MwNfont); - int tw = 0, mtw = 0; - if(ttf == NULL) return -1; - - while(text[0] != 0) { - int c; - int l = MwUTF8ToUTF32(text, &c); - if(l <= 0) break; - text += l; - if(c == '\n') { - tw = 0; - continue; - } - - FT_Load_Char(ttf->face, c, FT_LOAD_RENDER); - - tw += ttf->face->glyph->metrics.horiAdvance / 64; - if(tw > mtw) mtw = tw; - } - - return mtw; -} - -static int ttf_MwTextHeight(MwWidget handle, int count) { - ttf_t* ttf = MwGetVoid(handle, MwNfont); - if(ttf == NULL) return -1; - - return (ttf->face->height * 14 / ttf->face->units_per_EM) * count; -} -#endif - -void MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { - if(strlen(text) == 0) return; -#ifdef TTF - if(MwGetInteger(handle, MwNbitmapFont) || ttf_MwDrawText(handle, point, text, bold, align, color)) -#endif - bitmap_MwDrawText(handle, point, text, bold, align, color); -} - -int MwTextWidth(MwWidget handle, const char* text) { - /* TODO: check newline */ -#ifdef TTF - int st; - - if(!MwGetInteger(handle, MwNbitmapFont) && (st = ttf_MwTextWidth(handle, text)) != -1) return st; -#else - (void)handle; - -#endif - return MwUTF8Length(text) * FontWidth; -} - -int MwTextHeight(MwWidget handle, const char* text) { - int c = 1; - int i = 0; -#ifdef TTF - int st; -#endif - - (void)handle; - (void)text; - - while(text[i] != 0) { - int out; - int l = MwUTF8ToUTF32(text + i, &out); - if(l == 0) break; - i += l; - - if(out == '\n') c++; - } -#ifdef TTF - if(!MwGetInteger(handle, MwNbitmapFont) && (st = ttf_MwTextHeight(handle, c)) != -1) return st; -#endif - return FontHeight * c; -} - -void* MwFontLoad(unsigned char* data, unsigned int size) { -#if defined(USE_FREETYPE2) - ttf_t* ttf = malloc(sizeof(*ttf)); - ttf->data = malloc(size); - memcpy(ttf->data, data, size); - FT_Init_FreeType(&ttf->library); - FT_New_Memory_Face(ttf->library, ttf->data, size, 0, &ttf->face); - - FT_Set_Pixel_Sizes(ttf->face, 0, 14); - - return ttf; -#elif defined(USE_STB_TRUETYPE) - ttf_t* ttf = malloc(sizeof(*ttf)); - ttf->data = malloc(size); - memcpy(ttf->data, data, size); - stbtt_InitFont(&ttf->font, ttf->data, 0); - - ttf->scale = stbtt_ScaleForPixelHeight(&ttf->font, 16); - stbtt_GetFontVMetrics(&ttf->font, &ttf->ascent, &ttf->descent, 0); - - return ttf; -#else - (void)data; - (void)size; - return NULL; -#endif -} - -void MwFontFree(void* handle) { -#if defined(USE_FREETYPE2) - ttf_t* ttf = handle; - - FT_Done_Face(ttf->face); - FT_Done_FreeType(ttf->library); - - free(ttf->data); - free(ttf); -#elif defined(USE_STB_TRUETYPE) - ttf_t* ttf = handle; - - free(ttf->data); - free(ttf); -#else - (void)handle; -#endif -} diff --git a/src/font/boldfont.c b/src/text/font/boldfont.c similarity index 100% rename from src/font/boldfont.c rename to src/text/font/boldfont.c diff --git a/src/font/boldttf.c b/src/text/font/boldttf.c similarity index 100% rename from src/font/boldttf.c rename to src/text/font/boldttf.c diff --git a/src/font/font.c b/src/text/font/font.c similarity index 100% rename from src/font/font.c rename to src/text/font/font.c diff --git a/src/font/ttf.c b/src/text/font/ttf.c similarity index 100% rename from src/font/ttf.c rename to src/text/font/ttf.c diff --git a/src/text/ft2.c b/src/text/ft2.c new file mode 100644 index 00000000..c30f3da0 --- /dev/null +++ b/src/text/ft2.c @@ -0,0 +1,139 @@ +#ifdef USE_FREETYPE2 +#include + +#include +#include FT_FREETYPE_H + +struct _MwFLFont { + FT_Library library; + FT_Face face; + void* data; +}; +static int ft2_MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { + MwFLFont ttf = MwGetVoid(handle, bold ? MwNboldFont : MwNfont); + int tw, th; + unsigned char* px; + MwLLPixmap p; + MwRect r; + int x = 0, y = 0; + if(ttf == NULL) return 1; + + tw = MwTextWidth(handle, text); + th = MwTextHeight(handle, text); + px = malloc(tw * th * 4); + + memset(px, 0, tw * th * 4); + while(text[0] != 0) { + int c; + FT_Bitmap* bmp; + int cy, cx; + int l = MwUTF8ToUTF32(text, &c); + if(l <= 0) break; + text += l; + + if(c == '\n') { + x = 0; + y += ttf->face->height * 14 / ttf->face->units_per_EM; + continue; + } + + FT_Load_Char(ttf->face, c, FT_LOAD_RENDER); + bmp = &ttf->face->glyph->bitmap; + + for(cy = 0; cy < bmp->rows; cy++) { + for(cx = 0; cx < bmp->width; cx++) { + int ox = x + cx + ttf->face->glyph->bitmap_left; + int oy = y + (ttf->face->height * 14 / ttf->face->units_per_EM) - ttf->face->glyph->bitmap_top + cy + (ttf->face->descender * 14 / ttf->face->units_per_EM); + unsigned char* opx = &px[(oy * tw + ox) * 4]; + + opx[0] = color->common.red; + opx[1] = color->common.green; + opx[2] = color->common.blue; + opx[3] = bmp->buffer[cy * bmp->pitch + cx]; + } + } + + x += ttf->face->glyph->metrics.horiAdvance / 64; + } + + p = MwLoadRaw(handle, px, tw, th); + r.x = point->x; + r.y = point->y - th / 2; + r.width = tw; + r.height = th; + + if(align == MwALIGNMENT_CENTER) { + r.x -= tw / 2; + } else if(align == MwALIGNMENT_END) { + r.x -= tw; + } + + MwLLDrawPixmap(handle->lowlevel, &r, p); + MwLLDestroyPixmap(p); + free(px); + + return 0; +} + +static int ft2_MwTextWidth(MwWidget handle, const char* text) { + MwFLFont ttf = MwGetVoid(handle, MwNfont); + int tw = 0, mtw = 0; + if(ttf == NULL) return -1; + + while(text[0] != 0) { + int c; + int l = MwUTF8ToUTF32(text, &c); + if(l <= 0) break; + text += l; + if(c == '\n') { + tw = 0; + continue; + } + + FT_Load_Char(ttf->face, c, FT_LOAD_RENDER); + + tw += ttf->face->glyph->metrics.horiAdvance / 64; + if(tw > mtw) mtw = tw; + } + + return mtw; +} + +static int ft2_MwTextHeight(MwWidget handle, int count) { + MwFLFont ttf = MwGetVoid(handle, MwNfont); + if(ttf == NULL) return -1; + + return (ttf->face->height * 14 / ttf->face->units_per_EM) * count; +} + +static void* ft2_MwFontLoad(unsigned char* data, unsigned int size) { + MwFLFont ttf = malloc(sizeof(*ttf)); + ttf->data = malloc(size); + memcpy(ttf->data, data, size); + FT_Init_FreeType(&ttf->library); + FT_New_Memory_Face(ttf->library, ttf->data, size, 0, &ttf->face); + + FT_Set_Pixel_Sizes(ttf->face, 0, 14); + + return ttf; +} + +static void ft2_MwFontFree(void* handle) { + MwFLFont ttf = handle; + + FT_Done_Face(ttf->face); + FT_Done_FreeType(ttf->library); + + free(ttf->data); + free(ttf); +} + +int MWFL_FT2Setup(void) { + MwFLDrawText = ft2_MwDrawText; + MwFLTextWidth = ft2_MwTextWidth; + MwFLTextHeight = ft2_MwTextHeight; + MwFLFontLoad = ft2_MwFontLoad; + MwFLFontFree = ft2_MwFontFree; + return 0; +} +#endif diff --git a/src/text/stbtt.c b/src/text/stbtt.c new file mode 100644 index 00000000..ab9d539c --- /dev/null +++ b/src/text/stbtt.c @@ -0,0 +1,140 @@ +#ifdef USE_STB_TRUETYPE +#include + +#include "../../external/stb_truetype.h" + +struct _MwFLFont { + stbtt_fontinfo font; + void* data; + float scale; + int ascent; + int descent; +}; + +static int stbtt_MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { + MwFLFont ttf = MwGetVoid(handle, bold ? MwNboldFont : MwNfont); + unsigned char* px; + int tw, th; + MwRect r; + MwLLPixmap p; + int ax, lsb; + int x = 0, y = 0; + if(ttf == NULL) return 1; + + tw = MwTextWidth(handle, text); + th = MwTextHeight(handle, text); + px = malloc(tw * th * 4); + + memset(px, 0, tw * th * 4); + while(text[0] != 0) { + int c; + int x0, y0, x1, y1, cx, cy; + int ow, oh; + unsigned char* out; + + text += MwUTF8ToUTF32(text, &c); + if(c == '\n') { + x = 0; + y += (ttf->ascent - ttf->descent) * ttf->scale; + continue; + } + + stbtt_GetCodepointHMetrics(&ttf->font, c, &ax, &lsb); + + stbtt_GetCodepointBitmapBox(&ttf->font, c, ttf->scale, ttf->scale, &x0, &y0, &x1, &y1); + ow = x1 - x0; + oh = y1 - y0; + out = malloc(ow * oh); + stbtt_MakeCodepointBitmap(&ttf->font, out, ow, oh, ow, ttf->scale, ttf->scale, c); + + for(cy = 0; cy < oh; cy++) { + for(cx = 0; cx < ow; cx++) { + int ox = x + (lsb * ttf->scale) + cx; + int oy = y + (ttf->ascent * ttf->scale) + y0 + cy; + unsigned char* opx = &px[(oy * tw + ox) * 4]; + + opx[0] = color->common.red; + opx[1] = color->common.green; + opx[2] = color->common.blue; + opx[3] = out[cy * ow + cx]; + } + } + + x += ax * ttf->scale; + + free(out); + } + + p = MwLoadRaw(handle, px, tw, th); + r.x = point->x; + r.y = point->y - th / 2; + r.width = tw; + r.height = th; + + if(align == MwALIGNMENT_CENTER) { + r.x -= tw / 2; + } else if(align == MwALIGNMENT_END) { + r.x -= tw; + } + + MwLLDrawPixmap(handle->lowlevel, &r, p); + MwLLDestroyPixmap(p); + free(px); + + return 0; +} + +static int stbtt_MwTextWidth(MwWidget handle, const char* text) { + MwFLFont ttf = MwGetVoid(handle, MwNfont); + int ax, lsb; + int tw = 0; + if(ttf == NULL) return -1; + + while(text[0] != 0) { + int c; + text += MwUTF8ToUTF32(text, &c); + + stbtt_GetCodepointHMetrics(&ttf->font, c, &ax, &lsb); + + tw += ax * ttf->scale; + } + + return tw; +} + +static int stbtt_MwTextHeight(MwWidget handle, int count) { + MwFLFont ttf = MwGetVoid(handle, MwNfont); + if(ttf == NULL) return -1; + + return (ttf->ascent - ttf->descent) * ttf->scale * count; +} + +static void* stbtt_MwFontLoad(unsigned char* data, unsigned int size) { + MwFLFont ttf = malloc(sizeof(*ttf)); + ttf->data = malloc(size); + memcpy(ttf->data, data, size); + stbtt_InitFont(&ttf->font, ttf->data, 0); + + ttf->scale = stbtt_ScaleForPixelHeight(&ttf->font, 16); + stbtt_GetFontVMetrics(&ttf->font, &ttf->ascent, &ttf->descent, 0); + + return ttf; +} + +static void stbtt_MwFontFree(void* handle) { + MwFLFont ttf = handle; + + free(ttf->data); + free(ttf); +} + +int MwFL_STBTTSetup(void) { + MwFLDrawText = stbtt_MwDrawText; + MwFLTextWidth = stbtt_MwTextWidth; + MwFLTextHeight = stbtt_MwTextHeight; + MwFLFontLoad = stbtt_MwFontLoad; + MwFLFontFree = stbtt_MwFontFree; + return 0; +} + +#endif diff --git a/src/text/text.c b/src/text/text.c new file mode 100644 index 00000000..8d19de83 --- /dev/null +++ b/src/text/text.c @@ -0,0 +1,158 @@ +#include + +int (*MwFLDrawText)(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) = NULL; +int (*MwFLTextWidth)(MwWidget handle, const char* text) = NULL; +int (*MwFLTextHeight)(MwWidget handle, int count) = NULL; +void* (*MwFLFontLoad)(unsigned char* data, unsigned int size) = NULL; +void (*MwFLFontFree)(void* handle) = NULL; + +#if defined(USE_FREETYPE2) || defined(USE_STB_TRUETYPE) +#define TTF +#endif + +#define FontWidth 7 +#define FontHeight 14 + +static void bitmap_MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color); + +void MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { + if(strlen(text) == 0) return; +#ifdef TTF + if(MwFLDrawText) + if(MwGetInteger(handle, MwNbitmapFont) || MwFLDrawText(handle, point, text, bold, align, color)) +#endif + bitmap_MwDrawText(handle, point, text, bold, align, color); +} + +int MwTextWidth(MwWidget handle, const char* text) { + /* TODO: check newline */ +#ifdef TTF + int st; + + if(MwFLTextWidth) + if(!MwGetInteger(handle, MwNbitmapFont) && (st = MwFLTextWidth(handle, text)) != -1) return st; +#else + (void)handle; + +#endif + return MwUTF8Length(text) * FontWidth; +} + +int MwTextHeight(MwWidget handle, const char* text) { + int c = 1; + int i = 0; +#ifdef TTF + int st; +#endif + + (void)handle; + (void)text; + + while(text[i] != 0) { + int out; + int l = MwUTF8ToUTF32(text + i, &out); + if(l == 0) break; + i += l; + + if(out == '\n') c++; + } +#ifdef TTF + if(MwFLTextHeight) + if(!MwGetInteger(handle, MwNbitmapFont) && (st = MwFLTextHeight(handle, c)) != -1) return st; +#endif + return FontHeight * c; +} + +void* MwFontLoad(unsigned char* data, unsigned int size) { + if(MwFLFontLoad) + return MwFLFontLoad(data, size); + return NULL; +} + +void MwFontFree(void* handle) { + if(MwFLFontFree) + return MwFLFontFree(handle); +} + +static void bitmap_MwDrawText(MwWidget handle, MwPoint* point, const char* text, int bold, int align, MwLLColor color) { + int i = 0, x, y, sx, sy; + int tw, th; + unsigned char* px; + MwRect r; + MwLLPixmap p; + + if(strlen(text) == 0) text = " "; + tw = MwTextWidth(handle, text); + th = MwTextHeight(handle, text); + px = malloc(tw * th * 4); + + memset(px, 0, tw * th * 4); + + sx = 0; + sy = 0; + + while(text[i] != 0) { + int out; + i += MwUTF8ToUTF32(text + i, &out); + + if(out > 0xff) { + out = 0; + } + + if(out == '\n') { + sx = 0; + sy += FontHeight; + } else { + for(y = 0; y < FontHeight; y++) { + for(x = 0; x < FontWidth; x++) { + unsigned char* ppx = &px[((sy + y) * tw + sx + x) * 4]; + if((bold ? MwBoldFontData : MwFontData)[out].data[y] & (1 << ((FontWidth - 1) - x))) { + ppx[0] = color->common.red; + ppx[1] = color->common.green; + ppx[2] = color->common.blue; + ppx[3] = 255; + } else { + ppx[0] = 0; + ppx[1] = 0; + ppx[2] = 0; + ppx[3] = 0; + } + } + } + sx += FontWidth; + } + } + + p = MwLoadRaw(handle, px, tw, th); + r.x = point->x; + r.y = point->y - th / 2; + r.width = tw; + r.height = th; + + if(align == MwALIGNMENT_CENTER) { + r.x -= tw / 2; + } else if(align == MwALIGNMENT_END) { + r.x -= tw; + } + + MwLLDrawPixmap(handle->lowlevel, &r, p); + MwLLDestroyPixmap(p); + free(px); +} + +typedef int (*call_t)(void); +void MwFLSetup(void) { + call_t calls[] = { +#ifdef USE_FREETYPE2 + MWFL_FT2Setup, +#endif +#ifdef USE_STB_TRUETYPE + MwFL_STBTTSetup, +#endif + NULL}; + int i; + + for(i = 0; calls[i] != NULL; i++) { + if(calls[i]() == 0) return; + } +} diff --git a/src/widget/opengl.c b/src/widget/opengl.c index 8738b5d6..6d67344c 100644 --- a/src/widget/opengl.c +++ b/src/widget/opengl.c @@ -1,3 +1,5 @@ +#ifndef __APPLE__ + #include #include @@ -20,10 +22,13 @@ typedef struct gdiopengl { } gdiopengl_t; #endif #ifdef USE_X11 -typedef XVisualInfo* (*MWglXChooseVisual)(Display* dpy, int screen, int* attribList); -typedef GLXContext (*MWglXCreateContext)(Display* dpy, XVisualInfo* vis, GLXContext shareList, Bool direct); +typedef XVisualInfo* (*MWglXChooseVisual)(Display* dpy, int screen, + int* attribList); +typedef GLXContext (*MWglXCreateContext)(Display* dpy, XVisualInfo* vis, + GLXContext shareList, Bool direct); typedef void (*MWglXDestroyContext)(Display* dpy, GLXContext ctx); -typedef Bool (*MWglXMakeCurrent)(Display* dpy, GLXDrawable drawable, GLXContext ctx); +typedef Bool (*MWglXMakeCurrent)(Display* dpy, GLXDrawable drawable, + GLXContext ctx); typedef void (*MWglXSwapBuffers)(Display* dpy, GLXDrawable drawable); typedef void* (*MWglXGetProcAddress)(const GLubyte* procname); @@ -79,23 +84,24 @@ static int create(MwWidget handle) { o->lib = MwDynamicOpen("opengl32.dll"); - o->wglCreateContext = (MWwglCreateContext)(void*)MwDynamicSymbol(o->lib, "wglCreateContext"); - o->wglMakeCurrent = (MWwglMakeCurrent)(void*)MwDynamicSymbol(o->lib, "wglMakeCurrent"); - o->wglDeleteContext = (MWwglDeleteContext)(void*)MwDynamicSymbol(o->lib, "wglDeleteContext"); - o->wglGetProcAddress = (MWwglGetProcAddress)(void*)MwDynamicSymbol(o->lib, "wglGetProcAddress"); + o->wglCreateContext = + (MWwglCreateContext)(void*)MwDynamicSymbol(o->lib, "wglCreateContext"); + o->wglMakeCurrent = + (MWwglMakeCurrent)(void*)MwDynamicSymbol(o->lib, "wglMakeCurrent"); + o->wglDeleteContext = + (MWwglDeleteContext)(void*)MwDynamicSymbol(o->lib, "wglDeleteContext"); + o->wglGetProcAddress = (MWwglGetProcAddress)(void*)MwDynamicSymbol( + o->lib, "wglGetProcAddress"); o->gl = o->wglCreateContext(o->dc); } #endif #ifdef USE_X11 if(handle->lowlevel->common.type == MwLLBackendX11) { - int attribs[5]; - const char* glpath[] = { - "libGL.so", - "/usr/local/lib/libGL.so", - "/usr/X11R7/lib/libGL.so", - "/usr/pkg/lib/libGL.so"}; - int glincr = 0; + int attribs[5]; + const char* glpath[] = {"libGL.so", "/usr/local/lib/libGL.so", + "/usr/X11R7/lib/libGL.so", "/usr/pkg/lib/libGL.so"}; + int glincr = 0; x11opengl_t* o = r = malloc(sizeof(*o)); attribs[0] = GLX_RGBA; @@ -104,54 +110,73 @@ static int create(MwWidget handle) { attribs[3] = 24; attribs[4] = None; - while(glpath[glincr] != NULL && (o->lib = MwDynamicOpen(glpath[glincr++])) == NULL); + while(glpath[glincr] != NULL && + (o->lib = MwDynamicOpen(glpath[glincr++])) == NULL); - o->glXChooseVisual = (MWglXChooseVisual)MwDynamicSymbol(o->lib, "glXChooseVisual"); - o->glXCreateContext = (MWglXCreateContext)MwDynamicSymbol(o->lib, "glXCreateContext"); - o->glXDestroyContext = (MWglXDestroyContext)MwDynamicSymbol(o->lib, "glXDestroyContext"); - o->glXMakeCurrent = (MWglXMakeCurrent)MwDynamicSymbol(o->lib, "glXMakeCurrent"); - o->glXSwapBuffers = (MWglXSwapBuffers)MwDynamicSymbol(o->lib, "glXSwapBuffers"); - o->glXGetProcAddress = (MWglXGetProcAddress)MwDynamicSymbol(o->lib, "glXGetProcAddress"); + o->glXChooseVisual = + (MWglXChooseVisual)MwDynamicSymbol(o->lib, "glXChooseVisual"); + o->glXCreateContext = + (MWglXCreateContext)MwDynamicSymbol(o->lib, "glXCreateContext"); + o->glXDestroyContext = + (MWglXDestroyContext)MwDynamicSymbol(o->lib, "glXDestroyContext"); + o->glXMakeCurrent = + (MWglXMakeCurrent)MwDynamicSymbol(o->lib, "glXMakeCurrent"); + o->glXSwapBuffers = + (MWglXSwapBuffers)MwDynamicSymbol(o->lib, "glXSwapBuffers"); + o->glXGetProcAddress = + (MWglXGetProcAddress)MwDynamicSymbol(o->lib, "glXGetProcAddress"); /* XXX: fix this */ - o->visual = o->glXChooseVisual(handle->lowlevel->x11.display, DefaultScreen(handle->lowlevel->x11.display), attribs); - o->gl = o->glXCreateContext(handle->lowlevel->x11.display, o->visual, NULL, GL_TRUE); + o->visual = o->glXChooseVisual(handle->lowlevel->x11.display, + DefaultScreen(handle->lowlevel->x11.display), + attribs); + o->gl = o->glXCreateContext(handle->lowlevel->x11.display, o->visual, NULL, + GL_TRUE); } #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { - int err; - EGLint numConfigs; - EGLint majorVersion; - EGLint minorVersion; - EGLContext context; - EGLSurface surface; - EGLint fbAttribs[] = - { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_DEPTH_SIZE, 24, - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_NONE}; - EGLint contextAttribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 1, - EGL_CONTEXT_MAJOR_VERSION, 1, - EGL_CONTEXT_MINOR_VERSION, 1, - EGL_NONE}; + int err; + EGLint numConfigs; + EGLint majorVersion; + EGLint minorVersion; + EGLContext context; + EGLSurface surface; + EGLint fbAttribs[] = {EGL_SURFACE_TYPE, + EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_DEPTH_SIZE, + 24, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_BIT, + EGL_NONE}; + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, + 1, + EGL_CONTEXT_MAJOR_VERSION, + 1, + EGL_CONTEXT_MINOR_VERSION, + 1, + EGL_NONE}; EGLDisplay display; - waylandopengl_t* o = r = malloc(sizeof(*o)); - MwLL topmost_parent = handle->lowlevel->wayland.parent; - topmost_parent->wayland.always_render = MwTRUE; + waylandopengl_t* o = r = malloc(sizeof(*o)); + MwLL topmost_parent = handle->lowlevel->wayland.parent; + handle->lowlevel->wayland.always_render = MwTRUE; + topmost_parent->wayland.always_render = MwTRUE; while(topmost_parent->wayland.parent != NULL) { topmost_parent = topmost_parent->wayland.parent; topmost_parent->wayland.always_render = MwTRUE; } - display = eglGetDisplay((EGLNativeDisplayType)handle->lowlevel->wayland.display); + display = + eglGetDisplay((EGLNativeDisplayType)handle->lowlevel->wayland.display); if(display == EGL_NO_DISPLAY) { printf("ERROR: eglGetDisplay, %0X\n", eglGetError()); return MwFALSE; @@ -163,26 +188,31 @@ static int create(MwWidget handle) { } /* Get configs */ - if((eglGetConfigs(display, NULL, 0, &numConfigs) != EGL_TRUE) || (numConfigs == 0)) { + if((eglGetConfigs(display, NULL, 0, &numConfigs) != EGL_TRUE) || + (numConfigs == 0)) { printf("ERROR: eglGetConfigs, %0X\n", eglGetError()); return MwFALSE; } /* Choose config */ - if((eglChooseConfig(display, fbAttribs, &o->egl_config, 1, &numConfigs) != EGL_TRUE) || (numConfigs != 1)) { + if((eglChooseConfig(display, fbAttribs, &o->egl_config, 1, &numConfigs) != + EGL_TRUE) || + (numConfigs != 1)) { printf("ERROR: eglChooseConfig, %0X\n", eglGetError()); return MwFALSE; } - o->egl_window_native = - (EGLNativeWindowType)wl_egl_window_create(handle->lowlevel->wayland.framebuffer.surface, handle->lowlevel->wayland.ww, handle->lowlevel->wayland.wh); + o->egl_window_native = (EGLNativeWindowType)wl_egl_window_create( + handle->lowlevel->wayland.framebuffer.surface, + handle->lowlevel->wayland.ww, handle->lowlevel->wayland.wh); if(!o->egl_window_native) { printf("ERROR: wl_egl_window_create, EGL_NO_SURFACE\n"); return MwFALSE; } /* Create a surface */ - surface = eglCreateWindowSurface(display, o->egl_config, o->egl_window_native, NULL); + surface = eglCreateWindowSurface(display, o->egl_config, + o->egl_window_native, NULL); if(surface == EGL_NO_SURFACE) { printf("ERROR: eglCreateWindowSurface, %0X\n", eglGetError()); return MwFALSE; @@ -191,7 +221,8 @@ static int create(MwWidget handle) { eglBindAPI(EGL_OPENGL_API); /* Create a GL context */ - context = eglCreateContext(display, o->egl_config, EGL_NO_CONTEXT, contextAttribs); + context = eglCreateContext(display, o->egl_config, EGL_NO_CONTEXT, + contextAttribs); if(context == EGL_NO_CONTEXT) { printf("ERROR: eglCreateContext, %0X\n", eglGetError()); return MwFALSE; @@ -212,7 +243,8 @@ static int create(MwWidget handle) { MwSetDefault(handle); - while(w->parent != NULL) w = w->parent; + while(w->parent != NULL) + w = w->parent; w->berserk++; @@ -249,9 +281,10 @@ static void destroy(MwWidget handle) { } #endif - while(w->parent != NULL) w = w->parent; + while(w->parent != NULL) + w = w->parent; - w->berserk++; + w->berserk--; free(handle->internal); } @@ -266,7 +299,8 @@ static void mwOpenGLMakeCurrentImpl(MwWidget handle) { o->wglMakeCurrent(o->dc, o->gl); - if((swap_interval_ext = MwOpenGLGetProcAddress(handle, "wglSwapIntervalEXT")) != NULL) { + if((swap_interval_ext = + MwOpenGLGetProcAddress(handle, "wglSwapIntervalEXT")) != NULL) { swap_interval_ext(1); } } @@ -278,17 +312,22 @@ static void mwOpenGLMakeCurrentImpl(MwWidget handle) { void (*swap_interval_mesa)(unsigned int) = NULL; void (*swap_interval_sgi)(int) = NULL; - o->glXMakeCurrent(handle->lowlevel->x11.display, handle->lowlevel->x11.window, o->gl); + o->glXMakeCurrent(handle->lowlevel->x11.display, + handle->lowlevel->x11.window, o->gl); - if((swap_interval_ext = MwOpenGLGetProcAddress(handle, "glXSwapIntervalEXT")) != NULL) { - swap_interval_ext(handle->lowlevel->x11.display, handle->lowlevel->x11.window, 1); + if((swap_interval_ext = + MwOpenGLGetProcAddress(handle, "glXSwapIntervalEXT")) != NULL) { + swap_interval_ext(handle->lowlevel->x11.display, + handle->lowlevel->x11.window, 1); } - if((swap_interval_mesa = MwOpenGLGetProcAddress(handle, "glXSwapIntervalMESA")) != NULL) { + if((swap_interval_mesa = + MwOpenGLGetProcAddress(handle, "glXSwapIntervalMESA")) != NULL) { swap_interval_mesa(1); } - if((swap_interval_sgi = MwOpenGLGetProcAddress(handle, "glXSwapIntervalSGI")) != NULL) { + if((swap_interval_sgi = + MwOpenGLGetProcAddress(handle, "glXSwapIntervalSGI")) != NULL) { swap_interval_sgi(1); } } @@ -297,7 +336,8 @@ static void mwOpenGLMakeCurrentImpl(MwWidget handle) { if(handle->lowlevel->common.type == MwLLBackendWayland) { waylandopengl_t* o = handle->internal; - if(!eglMakeCurrent(o->egl_display, o->egl_surface, o->egl_surface, o->egl_context)) { + if(!eglMakeCurrent(o->egl_display, o->egl_surface, o->egl_surface, + o->egl_context)) { printf("ERROR: eglMakeCurrent, %0X\n", eglGetError()); } @@ -318,18 +358,19 @@ static void mwOpenGLSwapBufferImpl(MwWidget handle) { if(handle->lowlevel->common.type == MwLLBackendX11) { x11opengl_t* o = handle->internal; - o->glXSwapBuffers(handle->lowlevel->x11.display, handle->lowlevel->x11.window); + o->glXSwapBuffers(handle->lowlevel->x11.display, + handle->lowlevel->x11.window); } #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { waylandopengl_t* o = handle->internal; - eglSwapInterval(o->egl_display, 0); if(!eglSwapBuffers(o->egl_display, o->egl_surface)) { printf("ERROR: eglSwapBuffers, %0X\n", eglGetError()); }; - wl_egl_window_resize((struct wl_egl_window*)o->egl_window_native, handle->lowlevel->wayland.ww, handle->lowlevel->wayland.wh, 0, 0); - MwLLForceRender(handle->lowlevel); + wl_egl_window_resize((struct wl_egl_window*)o->egl_window_native, + handle->lowlevel->wayland.ww, + handle->lowlevel->wayland.wh, 0, 0); } #endif } @@ -357,7 +398,8 @@ static void* mwOpenGLGetProcAddressImpl(MwWidget handle, const char* name) { return NULL; } -static void func_handler(MwWidget handle, const char* name, void* out, va_list va) { +static void func_handler(MwWidget handle, const char* name, void* out, + va_list va) { if(strcmp(name, "mwOpenGLMakeCurrent") == 0) { mwOpenGLMakeCurrentImpl(handle); } @@ -370,25 +412,23 @@ static void func_handler(MwWidget handle, const char* name, void* out, va_list v } } -MwClassRec MwOpenGLClassRec = { - create, /* create */ - destroy, /* destroy */ - NULL, /* draw */ - NULL, /* click */ - NULL, /* parent_resize */ - NULL, /* prop_change */ - NULL, /* mouse_move */ - NULL, /* mouse_up */ - NULL, /* mouse_down */ - NULL, /* key */ - func_handler, /* execute */ - NULL, /* tick */ - NULL, /* resize */ - NULL, /* children_update */ - NULL, /* children_prop_change */ - NULL, /* clipboard */ - NULL, - NULL, - NULL, - NULL}; -MwClass MwOpenGLClass = &MwOpenGLClassRec; +MwClassRec MwOpenGLClassRec = {create, /* create */ + destroy, /* destroy */ + NULL, /* draw */ + NULL, /* click */ + NULL, /* parent_resize */ + NULL, /* prop_change */ + NULL, /* mouse_move */ + NULL, /* mouse_up */ + NULL, /* mouse_down */ + NULL, /* key */ + func_handler, /* execute */ + NULL, /* tick */ + NULL, /* resize */ + NULL, /* children_update */ + NULL, /* children_prop_change */ + NULL, /* clipboard */ + NULL, NULL, NULL, NULL}; +MwClass MwOpenGLClass = &MwOpenGLClassRec; + +#endif diff --git a/src/widget/opengl_cocoa.m b/src/widget/opengl_cocoa.m new file mode 100644 index 00000000..503fe5e7 --- /dev/null +++ b/src/widget/opengl_cocoa.m @@ -0,0 +1,130 @@ +#include "Mw/BaseTypes.h" +#include "Mw/Core.h" +#include "Mw/LowLevel/Cocoa.h" +#include "Mw/StringDefs.h" +#include +#include +#import + +@interface MacOpenGLWidget : NSObject { + NSOpenGLPixelFormat *pixelFormat; + NSOpenGLContext *glc; + MilskoCocoa *_win; +} + +- (MacOpenGLWidget *)initWithWindow:(MilskoCocoa *)w; +- (void)destroy; +- (void)makeCurrent; +- (void)swapBuffer; +- (void *)getProcAddressWithName:(const char *)name; + +@end + +static int create(MwWidget handle) { + void *r = NULL; + MwWidget w = handle; + + int x = MwGetInteger(w, MwNx); + int y = MwGetInteger(w, MwNy); + int width = MwGetInteger(w, MwNwidth); + int height = MwGetInteger(w, MwNheight); + + printf("%d %d\n", width, height); + + handle->internal = + [[MacOpenGLWidget alloc] initWithWindow:handle->lowlevel->cocoa.real]; + handle->lowlevel->common.copy_buffer = 0; + + MwSetDefault(handle); + + while (w->parent != NULL) + w = w->parent; + + w->berserk++; + + return 0; +} + +static void destroy(MwWidget handle) { + [(MacOpenGLWidget *)handle->internal destroy]; +} + +static void mwOpenGLMakeCurrentImpl(MwWidget handle) { + [(MacOpenGLWidget *)handle->internal makeCurrent]; +} + +static void mwOpenGLSwapBufferImpl(MwWidget handle) { + [(MacOpenGLWidget *)handle->internal swapBuffer]; +} + +static void *mwOpenGLGetProcAddressImpl(MwWidget handle, const char *name) { + return [(MacOpenGLWidget *)handle->internal getProcAddressWithName:name]; +} + +static void func_handler(MwWidget handle, const char *name, void *out, + va_list va) { + if (strcmp(name, "mwOpenGLMakeCurrent") == 0) { + mwOpenGLMakeCurrentImpl(handle); + } + if (strcmp(name, "mwOpenGLSwapBuffer") == 0) { + mwOpenGLSwapBufferImpl(handle); + } + if (strcmp(name, "mwOpenGLGetProcAddress") == 0) { + const char *_name = va_arg(va, const char *); + *(void **)out = mwOpenGLGetProcAddressImpl(handle, _name); + } +} + +@implementation MacOpenGLWidget + +- (MacOpenGLWidget *)initWithWindow:(MilskoCocoa *)w { + NSOpenGLPixelFormatAttribute pixelFormatAttributes[] = { + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAStencilSize, 8, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, 0}; + self->pixelFormat = + [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttributes]; + self->glc = + [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + [self->glc setView:[w getView]]; + self->_win = w; + return self; +}; +- (void)destroy { +}; +- (void)makeCurrent { + [self->glc makeCurrentContext]; +}; +- (void)swapBuffer { + [self->glc flushBuffer]; +}; +- (void *)getProcAddressWithName:(const char *)name { + if (NSIsSymbolNameDefined(name)) { + return NSAddressOfSymbol(NSLookupAndBindSymbol(name)); + } else { + return NULL; + } +}; + +@end + +MwClassRec MwOpenGLClassRec = {create, /* create */ + destroy, /* destroy */ + NULL, /* draw */ + NULL, /* click */ + NULL, /* parent_resize */ + NULL, /* prop_change */ + NULL, /* mouse_move */ + NULL, /* mouse_up */ + NULL, /* mouse_down */ + NULL, /* key */ + func_handler, /* execute */ + NULL, /* tick */ + NULL, /* resize */ + NULL, /* children_update */ + NULL, /* children_prop_change */ + NULL, /* clipboard */ + NULL, NULL, NULL, NULL}; +MwClass MwOpenGLClass = &MwOpenGLClassRec; \ No newline at end of file diff --git a/src/widget/vulkan.c b/src/widget/vulkan.c index b78e7429..e2dfa4c8 100644 --- a/src/widget/vulkan.c +++ b/src/widget/vulkan.c @@ -231,7 +231,7 @@ static MwErrorEnum vulkan_instance_setup(MwWidget handle, vulkan_t* o) { arrput(enabledExtensions, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); /* take this opprutunity to set the widget to always render */ - topmost_parent->wayland.always_render = MwTRUE; + handle->lowlevel->wayland.always_render = MwTRUE; while(topmost_parent->wayland.parent != NULL) { topmost_parent = topmost_parent->wayland.parent; diff --git a/tools/genmk.pl b/tools/genmk.pl index 77545d8f..1cd0a9ed 100755 --- a/tools/genmk.pl +++ b/tools/genmk.pl @@ -147,7 +147,7 @@ scan("src/icon"); scan("src/cursor"); scan("src/widget"); scan("src/text"); -scan("src/font"); +scan("src/text/font"); scan("src/dialog"); scan("src/abstract"); push(@cfiles, "src/backend/gdi.c"); diff --git a/tools/readme.pl b/tools/readme.pl index 573d9573..7070312d 100755 --- a/tools/readme.pl +++ b/tools/readme.pl @@ -59,8 +59,10 @@ l(""); c("Requirements"); l(""); l( -" Milsko requires the Windows environment with GDI (so anything NT or 9x) or the Unix-like environment with X11 for runtime." -); +" Milsko requires either"); +l(" * A Windows environment with GDI (so anything NT or 9x)"); +l(" * A MacOS environment with Cocoa (10.4 or above supported)"); +l(" * A Unix-like environment with X11 for runtime."); l(""); l(" To build Milsko for Windows, you must have one of following compilers:"); l(" * Visual C++ 6.0 or newer"); @@ -68,7 +70,7 @@ l(" * Borland C++ 5.5 or newer"); l(" * Open Watcom 2.0 or newer"); l(" * MinGW-w64"); l(""); -l(" and for Unix-like:"); +l(" and for Unix-like and MacOS:"); l(" * GNU C Compiler"); l(" * Clang"); l(""); @@ -105,8 +107,8 @@ l("1) Run `wmake -f WatMakefile'."); h("D. MinGW-w64/GCC/Clang"); l("1) Determine if you need Vulkan and/or OpenGL."); l(""); -l("2) Run `./Makefile.pl'."); -l(" For help, run `./Makefile.pl --help'."); +l("2) Run `./configure'."); +l(" For help, run `./configure --help'."); l(""); l("3) Run `make'."); l("");