diff --git a/examples/microwindows/Kconfig b/examples/microwindows/Kconfig new file mode 100644 index 00000000000..f4b1d110786 --- /dev/null +++ b/examples/microwindows/Kconfig @@ -0,0 +1,25 @@ +config EXAMPLES_MICROWINDOWS + tristate "Microwindows example" + default n + depends on GRAPHICS_MICROWINDOWS + ---help--- + Enable the Microwindows example application + +if EXAMPLES_MICROWINDOWS + +config EXAMPLES_MICROWINDOWS_PROGNAME + string "Program name" + default "mwexample" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_MICROWINDOWS_PRIORITY + int "Microwindows task priority" + default 100 + +config EXAMPLES_MICROWINDOWS_STACKSIZE + int "Microwindows stack size" + default 32768 + +endif diff --git a/examples/microwindows/Make.defs b/examples/microwindows/Make.defs new file mode 100644 index 00000000000..51ff2f8fd1f --- /dev/null +++ b/examples/microwindows/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/hello/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_MICROWINDOWS),) +CONFIGURED_APPS += $(APPDIR)/examples/microwindows +endif diff --git a/examples/microwindows/Makefile b/examples/microwindows/Makefile new file mode 100644 index 00000000000..181b63d9e76 --- /dev/null +++ b/examples/microwindows/Makefile @@ -0,0 +1,55 @@ +############################################################################ +# apps/examples/microwindows/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_EXAMPLES_MICROWINDOWS_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_MICROWINDOWS_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_MICROWINDOWS_STACKSIZE) +MODULE = y + +MAINSRC = mwdemo_main.c +CSRCS += mwdemo.c +MW_IMAGES = car8 zion208 penguin under4 cs1 rle8 microwin +CSRCS += $(MW_IMAGES:%=$(MW_IMGDIR)/%.c) + +# Generate image .c files from .bmp +MW_SRC = $(APPDIR)/graphics/microwindows/microwindows/src +MW_IMGDIR = images +MW_CONVBMP = convbmp$(HOSTEXEEXT) + +$(MW_CONVBMP): $(MW_SRC)/images/tools/convbmp.c + $(HOSTCC) $(HOSTCFLAGS) -I$(MW_SRC)/include $< -o $@ + +$(MW_IMGDIR)/%.c: $(MW_SRC)/images/demos/mwin/%.bmp $(MW_CONVBMP) + mkdir -p $(MW_IMGDIR) + $(CURDIR)/$(MW_CONVBMP) -o $@ $< + +clean:: + $(call DELFILE, $(MW_IMAGES:%=$(MW_IMGDIR)/%.c)) + $(Q) rmdir $(MW_IMGDIR) 2>/dev/null || true + $(call DELFILE, $(MW_CONVBMP)) + +CFLAGS += -Wno-undef +CFLAGS += -DNOGDI + +include $(APPDIR)/Application.mk diff --git a/examples/microwindows/mwdemo.c b/examples/microwindows/mwdemo.c new file mode 100644 index 00000000000..4ef298ec9c0 --- /dev/null +++ b/examples/microwindows/mwdemo.c @@ -0,0 +1,532 @@ + +/* + * Copyright (c) 1999, 2000, 2001, 2010 Greg Haerr + * Portions Copyright (c) 2002 by Koninklijke Philips Electronics N.V. + * + * Demo program for Microwindows + * + * GB: 10-14-2004: Modified to store degrees data on each window, + * for new Timers features. + */ +#include +#include +#include +#include "uni_std.h" +#define MWINCLUDECOLORS +#include "windows.h" +#include "device.h" + +#if DOS_TURBOC +unsigned _stklen = 4096; +#endif + +/* define either GRAPH3D demo or CHILD control demo*/ +#define GRAPH3D 1 /* 3d graphics demo (requires floating point) */ +#define CONTROLS 0 /* win32 controls demo */ + +/* add clipping to GRAPH3D demo*/ +#define CLIPDEMO 0 /* set for region clipping demo */ + +/* CONTROLS demo options*/ +#define ARCDEMO 1 /* add arc drawing to CONTROLS demo */ +#define USEBLIT 1 /* use blit rather than DrawDIB() */ +#define IMAGE 0 /* add 256 color image to CONTROLS demo */ + +/* test timer system with either demo*/ +#define TIMERDEMO 1 /* set for WM_TIMER demo */ + +/* not used*/ +#define CLIENT3D 0 /* old client draw test */ + +#if RTEMS +# undef GRAPH3D +# undef CONTROLS +# define CONTROLS 1 /* win32 controls demo */ +# undef IMAGE +# define IMAGE 1 /* 256 color image demo */ +# undef USEBLIT +# define USEBLIT 0 + /* use blit rather than DrawDIB() */ +#endif + +#if GRAPH3D +# include "graph3d.h" +#endif + +extern MWIMAGEHDR image_car8; +extern MWIMAGEHDR image_zion208; +extern MWIMAGEHDR image_penguin; +extern MWIMAGEHDR image_under4; +extern MWIMAGEHDR image_microwin; +extern MWIMAGEHDR image_cs1; +extern MWIMAGEHDR image_rle8; + +#if CONTROLS +# if ELKS | MSDOS +PMWIMAGEHDR image = &image_cs1; /* 2 color bitmap for 16 color systems */ +# else +PMWIMAGEHDR image = &image_penguin; +# endif +#endif + +#if IMAGE +PMWIMAGEHDR image2 = &image_zion208; +#endif + +#define APPCLASS "test" +#define APPCHILD "test2" + +/* forward decls*/ +LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wp, LPARAM lp); +LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT uMsg, WPARAM wp, LPARAM lp); + +int MwUserInit(int ac, char **av) +{ + /* test user init procedure - do nothing */ + return 0; +} + +static int RegisterAppClass(HINSTANCE hInstance) +{ + WNDCLASS wc; + + /* register builtin controls and dialog classes */ + MwInitializeDialogs(hInstance); + + wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW; + wc.lpfnWndProc = (WNDPROC) WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(LONG_PTR); + wc.hInstance = 0; + wc.hIcon = 0; /* LoadIcon(GetHInstance(), MAKEINTRESOURCE( + * 1)); */ + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = APPCLASS; + RegisterClass(&wc); + +#if CONTROLS + wc.lpfnWndProc = (WNDPROC) ChildWndProc; + wc.lpszClassName = APPCHILD; + return RegisterClass(&wc); +#endif + return 1; +} + +static HWND CreateAppWindow(void) +{ + HWND hwnd; + static int nextid = 1; + int width, height; + RECT r; + GetWindowRect(GetDesktopWindow(), &r); + width = height = r.right / 2; + + hwnd = CreateWindowEx(0L, APPCLASS, + "Microwindows Application", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, + width, height, + NULL, (HMENU) (LONG_PTR) nextid++, NULL, NULL); + +#if CONTROLS + if (hwnd +# if GRAPH3D + && (nextid & 03) != 2 +# endif + ) + { + CreateWindowEx(0L, APPCHILD, + "", + WS_BORDER | WS_CHILD | WS_VISIBLE, + 4, 4, width / 3 - 6, height / 3, + hwnd, (HMENU) 2, NULL, NULL); + CreateWindowEx(0L, APPCHILD, + "", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width / 3, height / 3, width / 3 - 6, height / 3, + hwnd, (HMENU) 3, NULL, NULL); + CreateWindowEx(0L, APPCHILD, + "", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width * 3 / 5, height * 3 / 5, + width * 2 / 3, height * 2 / 3, + hwnd, (HMENU) 4, NULL, NULL); + CreateWindowEx(0L, "EDIT", + "OK", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width * 5 / 8, 10, 100, 18, hwnd, (HMENU) 5, NULL, NULL); + CreateWindowEx(0L, "PROGBAR", + "OK", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width * 5 / 8, 32, 100, 18, hwnd, (HMENU) 6, NULL, NULL); + + HWND hlist = CreateWindowEx(0L, "LISTBOX", + "OK", + WS_HSCROLL | WS_VSCROLL | WS_BORDER | WS_CHILD + | WS_VISIBLE, + width * 5 / 8, 54, 100, 48, + hwnd, (HMENU) 7, NULL, NULL); + SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM) (LPSTR) "Cherry"); + SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM) (LPSTR) "Apple"); + SendMessage(hlist, LB_ADDSTRING, 0, (LPARAM) (LPSTR) "Orange"); + + CreateWindowEx(0L, "BUTTON", + "Cancel", + BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, + width * 5 / 8 + 50, 106, 50, 14, + hwnd, (HMENU) 8, NULL, NULL); + } +#endif + return hwnd; +} + +#if CONTROLS +LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + RECT rc; + PAINTSTRUCT ps; + + switch (msg) + { + case WM_PAINT: + BeginPaint(hwnd, &ps); + GetClientRect(hwnd, &rc); +# if USEBLIT + { + HDC hdcMem; + HBITMAP hbmp, hbmpOrg; + + /* redirect painting to offscreen dc, then use blit function */ + hdcMem = CreateCompatibleDC(ps.hdc); + /* + * Note: rc.right, rc.bottom happens to be smaller than image + * width/height. We use the image size, so we can stretchblit + * from the whole image. + */ + hbmp = CreateCompatibleBitmap(hdcMem, image->width, image->height); + hbmpOrg = SelectObject(hdcMem, hbmp); + + /* draw onto offscreen dc */ + DrawDIB(hdcMem, 0, 0, image); + + /* blit offscreen with physical screen */ + // BitBlt(ps.hdc, 0, 0, rc.right*4/5, rc.bottom*4/5, hdcMem, + // 0, 0, MWROP_COPY); + StretchBlt(ps.hdc, 0, 0, rc.right * 4 / 5, rc.bottom * 4 / 5, hdcMem, + 0, 0, image->width, image->height, MWROP_COPY); + DeleteObject(SelectObject(hdcMem, hbmpOrg)); + DeleteDC(hdcMem); + } +# else + DrawDIB(ps.hdc, rc.left, rc.top, image); +# endif + EndPaint(hwnd, &ps); + break; + default: + return DefWindowProc(hwnd, msg, wp, lp); + } + return (0); +} +#endif + +typedef struct + { + int startdegrees; + int enddegrees; + unsigned long reserved; + } demoWndData, *pdemoWndData; + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + PAINTSTRUCT ps; + HDC hdc; +#if CLIENT3D | IMAGE | GRAPH3D + RECT rc; +#endif +#if GRAPH3D + static int countup = 1; + int id; + static vec1 gx, gy; +#endif + static POINT mousept; + pdemoWndData pData; + + switch (msg) + { + case WM_CREATE: + pData = (pdemoWndData) malloc(sizeof(demoWndData)); + SetWindowLongPtr(hwnd, 0, (LONG_PTR) pData); + mousept.x = 60; + mousept.y = 20; + pData->startdegrees = 0; + pData->enddegrees = 30; +#if TIMERDEMO + SetTimer(hwnd, 1, (50 + random() % 250), NULL); +#endif + break; + + case WM_TIMER: + pData = (pdemoWndData) (LONG_PTR) GetWindowLongPtr(hwnd, 0); +#if GRAPH3D + GetClientRect(hwnd, &rc); + if (countup) + { + mousept.y += 20; + if (mousept.y >= rc.bottom) + { + mousept.y -= 20; + countup = 0; + } + } + else + { + mousept.y -= 20; + if (mousept.y < 20) + { + mousept.y += 20; + countup = 1; + } + } + SendMessage(hwnd, WM_MOUSEMOVE, 0, MAKELONG(mousept.x, mousept.y)); +#elif ARCDEMO + pData->startdegrees += 10; + if (pData->startdegrees >= 360) + pData->startdegrees = 0; + pData->enddegrees += 15; + if (pData->enddegrees >= 360) + pData->enddegrees = 0; + InvalidateRect(hwnd, NULL, TRUE); +#endif + break; + case WM_DESTROY: + KillTimer(hwnd, 1); + pData = (pdemoWndData) (LONG_PTR) GetWindowLongPtr(hwnd, 0); + free(pData); + SetWindowLongPtr(hwnd, 0, (LONG_PTR) 0); + break; + case WM_SIZE: + break; + case WM_MOVE: + break; + +#if CLIENT3D + case WM_SETFOCUS: + PostMessage((HWND) wp, WM_PAINT, 0, 0L); + break; + case WM_KILLFOCUS: + PostMessage((HWND) wp, WM_PAINT, 0, 0L); + break; + case WM_ERASEBKGND: + if (GetFocus() != hwnd) + return DefWindowProc(hwnd, msg, wp, lp); + return 1; +#endif +#if GRAPH3D + case WM_ERASEBKGND: + if ((GetWindowLong(hwnd, GWL_ID) & 03) == 1) + { + return 1; + } + return DefWindowProc(hwnd, msg, wp, lp); +#endif + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + +#if CLIENT3D + if (GetFocus() == hwnd) + { + GetClientRect(hwnd, &rc); + Draw3dShadow(hdc, rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top, + GetSysColor(COLOR_3DDKSHADOW), + GetSysColor(COLOR_3DLIGHT)); + InflateRect(&rc, -1, -1); + FillRect(hdc, &rc, GetStockObject(GRAY_BRUSH)); + } +#endif +#if IMAGE + GetClientRect(hwnd, &rc); + DrawDIB(hdc, rc.left + 2, rc.top + 2, image2); +#endif +#if GRAPH3D + id = (int)GetWindowLong(hwnd, GWL_ID) & 03; + init3(hdc, id == 1 ? hwnd : NULL); + switch (id) + { + case 0: + rose(1.0, 7, 13); + break; + case 1: + /* look3(0.5, 0.7, 1.5); */ + /* look3(0.2, -2 * gy, 1.0+gx); */ + look3(-2 * gx, -2 * gy, 1.2); + drawgrid(-8.0, 8.0, 10, -8.0, 8.0, 10); + break; + case 2: + setcolor3(BLACK); + circle3(1.0); + break; + case 3: + setcolor3(BLUE); + daisy(1.0, 20); + break; + } +# if CLIPDEMO + if (id == 1) + { + HRGN hrgn, hrgn2; + + /* create circular clip region for effect */ + GetClientRect(hwnd, &rc); + InflateRect(&rc, -80, -80); + switch ((int)GetWindowLong(hwnd, GWL_ID)) + { + default: + hrgn = CreateEllipticRgnIndirect(&rc); + break; + case 5: + hrgn = CreateRoundRectRgn(rc.left, rc.top, + rc.right, rc.bottom, 100, 100); + break; + case 2: + hrgn = CreateRectRgnIndirect(&rc); + break; + } + + /* erase background, clip out blit area */ + GetClientRect(hwnd, &rc); + hrgn2 = CreateRectRgnIndirect(&rc); + SelectClipRgn(hdc, hrgn2); + ExtSelectClipRgn(hdc, hrgn, RGN_XOR); + DeleteObject(hrgn2); + + GetClientRect(hwnd, &rc); + FillRect(hdc, &rc, GetStockObject(BLACK_BRUSH)); + + /* clip in only blit area */ + SelectClipRgn(hdc, hrgn); + DeleteObject(hrgn); + } +# endif /* CLIPDEMO */ + paint3(hdc); +#elif ARCDEMO + { + int x, y, w, h; + RECT rc; + + if (hdc != NULL) + { + pData = (pdemoWndData) GetWindowLongPtr(hwnd, 0); + GetWindowRect(hwnd, &rc); + rc.top += 13; + InflateRect(&rc, -3, -3); + /* Ellipse(hdc, 0, 0, rc.right-rc.left, rc.bottom-rc.top); */ + /* Arc(hdc, 0, 0, rc.right-rc.left, rc.bottom-rc.top, 0,0, 0,0); */ + /* Pie(hdc, 0, 0, rc.right-rc.left, rc.bottom-rc.top, 0,0, 0,0); */ + + x = rc.left; + y = rc.top; + w = rc.right - rc.left; + h = rc.bottom - rc.top; + w += 10; + GdSetForegroundColor(hdc->psd, RGB(0, 255, 0)); + GdArcAngle(hdc->psd, x + w / 2, y + h / 2, w / 2, h / 2, + pData->startdegrees * 64, pData->enddegrees * 64, MWPIE); + GdSetForegroundColor(hdc->psd, RGB(0, 0, 0)); + GdArcAngle(hdc->psd, x + w / 2, y + h / 2, w / 2, h / 2, + pData->startdegrees * 64, pData->enddegrees * 64, + MWARCOUTLINE); + /* GdSetForegroundColor(hdc->psd, RGB(255,255,255))); */ + /* GdPoint(hdc->psd, x+w/2, y+h/2); */ + } + EndPaint(hwnd, &ps); + break; + } +#endif /* ARCDEMO */ + EndPaint(hwnd, &ps); + break; + + case WM_MOUSEMOVE: +#if GRAPH3D + pData = (pdemoWndData) (LONG_PTR) GetWindowLongPtr(hwnd, 0); + if ((GetWindowLong(hwnd, GWL_ID) & 03) == 1) + { + POINT pt; + + POINTSTOPOINT(pt, lp); + GetClientRect(hwnd, &rc); + gx = (vec1) pt.x / rc.right; + gy = (vec1) pt.y / rc.bottom; + InvalidateRect(hwnd, NULL, FALSE); + mousept.x = pt.x; + mousept.y = pt.y; + } +#endif + break; + case WM_LBUTTONDOWN: + break; + case WM_LBUTTONUP: + break; + case WM_RBUTTONDOWN: + break; + default: + return DefWindowProc(hwnd, msg, wp, lp); + } + return 0; +} + +#if EMSCRIPTEN && MULTIAPP +# define WinMain mwdemo_WinMain +#endif + +int WINAPI +WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, + int nShowCmd) +{ + MSG msg; + HWND hwnd; + RECT rc; + + srandom(time(NULL)); + RegisterAppClass(hInstance); + GetWindowRect(GetDesktopWindow(), &rc); + + /* create penguin window */ + CreateWindowEx(0L, APPCHILD, "", WS_BORDER | WS_VISIBLE, + rc.right - 130 - 1, rc.bottom - 153 - 1, 130, 153, + GetDesktopWindow(), (HMENU) 1000, NULL, NULL); + +#if CONTROLS + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); +#endif +#if !MULTIAPP + /* set background wallpaper */ + MwSetDesktopWallpaper(&image_microwin); + /* MwSetDesktopWallpaper(&image_under4); */ + /* MwSetDesktopWallpaper(&image_car8); */ +#endif + + hwnd = CreateAppWindow(); + GetWindowRect(hwnd, &rc); + OffsetRect(&rc, 50, 50); + MoveWindow(hwnd, rc.left, rc.top, rc.bottom - rc.top, + rc.right - rc.left, TRUE); + +#if !MULTIAPP + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#endif + return 0; +} diff --git a/examples/microwindows/mwdemo_main.c b/examples/microwindows/mwdemo_main.c new file mode 100644 index 00000000000..08da63cda5c --- /dev/null +++ b/examples/microwindows/mwdemo_main.c @@ -0,0 +1,25 @@ + +/**************************************************************************** + * apps/examples/microwindows/mwdemo_main.c + * + * NuttX entry point for mwdemo (Microwindows Win32 API demo) + ****************************************************************************/ + +#include +#include "windows.h" + +extern int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nShowCmd); + +int main(int argc, FAR char *argv[]) +{ + int ret; + + invoke_WinMain_Start(argc, (char **)argv); + + ret = WinMain(0, NULL, (LPSTR) (argc > 0 ? argv[0] : ""), 0); + + invoke_WinMain_End(); + + return ret; +} diff --git a/graphics/microwindows/.gitignore b/graphics/microwindows/.gitignore new file mode 100644 index 00000000000..0cb9c84040c --- /dev/null +++ b/graphics/microwindows/.gitignore @@ -0,0 +1,2 @@ +/microwindows +/*.zip diff --git a/graphics/microwindows/Kconfig b/graphics/microwindows/Kconfig new file mode 100644 index 00000000000..28d74096fa6 --- /dev/null +++ b/graphics/microwindows/Kconfig @@ -0,0 +1,10 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig GRAPHICS_MICROWINDOWS + bool "Microwindows or the Nano-X Window System" + default n + ---help--- + Enable Microwindows or the Nano-X Window System diff --git a/graphics/microwindows/Make.defs b/graphics/microwindows/Make.defs new file mode 100644 index 00000000000..d5931558c56 --- /dev/null +++ b/graphics/microwindows/Make.defs @@ -0,0 +1,44 @@ +############################################################################ +# apps/graphics/microwindows/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_GRAPHICS_MICROWINDOWS),) +CONFIGURED_APPS += $(APPDIR)/graphics/microwindows + +# It allows `microwindows/src/include` import. + +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/include +CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/include +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/drivers + +ifeq ($(CONFIG_VIDEO_FB),) +$(error Microwindows requires CONFIG_VIDEO_FB=y) +endif + +HAVE_FNT_SUPPORT = Y +CFLAGS += -DHAVE_FNT_SUPPORT=1 +CFLAGS += -DHAVE_FILEIO=1 +CFLAGS += -DUNIX=1 + +CFLAGS += -DSCREEN_PIXTYPE=MWPF_TRUECOLORARGB +CFLAGS += -DSCREEN_DEPTH=8 + +endif diff --git a/graphics/microwindows/Makefile b/graphics/microwindows/Makefile new file mode 100644 index 00000000000..a99959d973d --- /dev/null +++ b/graphics/microwindows/Makefile @@ -0,0 +1,130 @@ +############################################################################ +# apps/graphics/microwindows/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# LVGL graphic library + +MICROWINDOWS_DIR = . +MICROWINDOWS_DIR_NAME = microwindows + +#-include ./lvgl/lvgl.mk + +# Set up build configuration and environment + +WD := ${shell echo $(CURDIR) | sed -e 's/ /\\ /g'} + +MICROWINDOWS_COMMIT_HASH := 7a2ff57052d8663cbca4863709a8ee75bf9fa0c2 + +#CONFIG_GRAPH_MICROWINDOWS_URL ?= "https://codeload.github.com/ghaerr/microwindows/zip/refs/heads/master" +CONFIG_GRAPH_MICROWINDOWS_URL ?= https://codeload.github.com/ghaerr/microwindows/zip/$(MICROWINDOWS_COMMIT_HASH) + +MICROWINDOWS_TARBALL := microwindows-$(MICROWINDOWS_COMMIT_HASH).zip + +MICROWINDOWS_UNPACKNAME = microwindows +UNPACK ?= unzip -o $(if $(V),,-q) +CURL ?= curl -L $(if $(V),,-Ss) + +MICROWINDOWS_UNPACKDIR = $(WD)/$(MICROWINDOWS_UNPACKNAME) + +$(MICROWINDOWS_TARBALL): + $(ECHO_BEGIN)"Downloading: $(MICROWINDOWS_TARBALL)" + $(Q) $(CURL) -o $(MICROWINDOWS_TARBALL) $(CONFIG_GRAPH_MICROWINDOWS_URL) + $(ECHO_END) + +$(MICROWINDOWS_UNPACKNAME): $(MICROWINDOWS_TARBALL) + $(ECHO_BEGIN)"Unpacking: $(MICROWINDOWS_TARBALL) -> $(MICROWINDOWS_UNPACKNAME)" + $(Q) $(UNPACK) $(MICROWINDOWS_TARBALL) + $(Q) mv microwindows-$(MICROWINDOWS_COMMIT_HASH) $(MICROWINDOWS_UNPACKNAME) + $(Q) touch $(MICROWINDOWS_UNPACKNAME) + $(ECHO_END) + +# Download and unpack tarball if no git repo found +ifeq ($(wildcard $(MICROWINDOWS_UNPACKNAME)/.git),) +context:: $(MICROWINDOWS_UNPACKNAME) +endif + +CFLAGS += -DRTEMS=0 -DPSP=0 -D__ECOS=0 -D__MINGW32__=0 +CFLAGS += -DELKS=0 -D_MINIX=0 -DMSDOS=0 -DLINUX=0 +CFLAGS += -DMACOSX=0 -DTRIMEDIA=0 + +CFLAGS += -Wno-undef +CFLAGS += -Wno-shadow + +MW_DIR_OBJ = $(WD)/$(MICROWINDOWS_DIR_NAME)/src +MW_LOCAL_DIR := mwin-local + +-include microwindows/src/engine/Objects.rules +-include microwindows/src/drivers/Objects.rules +-include microwindows/src/fonts/Objects.rules + +CSRCS += $(MW_CORE_OBJS:%.o=%.c) +CSRCS += $(MW_LOCAL_DIR)/scr_nuttx.c +CSRCS += $(MW_LOCAL_DIR)/mou_nuttx.c +CSRCS += $(MW_LOCAL_DIR)/kbd_nuttx.c + +ifneq ($(CONFIG_EXAMPLES_MICROWINDOWS),) + +# Win32 API used by mwdemo.c +MWIN_CORE_SRCS = microwindows/src/mwin/winmain.c +MWIN_CORE_SRCS += microwindows/src/mwin/winuser.c +MWIN_CORE_SRCS += microwindows/src/mwin/wingdi.c +MWIN_CORE_SRCS += microwindows/src/mwin/winexpos.c +MWIN_CORE_SRCS += microwindows/src/mwin/winclip.c +MWIN_CORE_SRCS += microwindows/src/mwin/winevent.c +MWIN_CORE_SRCS += microwindows/src/mwin/windefw.c +MWIN_CORE_SRCS += microwindows/src/mwin/winrgn.c +MWIN_CORE_SRCS += microwindows/src/mwin/winfont.c +MWIN_CORE_SRCS += microwindows/src/mwin/winsbar.c +MWIN_CORE_SRCS += microwindows/src/mwin/winres.c + +# mwin winlib +WINLIB_SRCS = microwindows/src/mwin/winlib/draw3d.c +WINLIB_SRCS += microwindows/src/mwin/winlib/fastfill.c +WINLIB_SRCS += microwindows/src/mwin/winlib/windlg.c +WINLIB_SRCS += microwindows/src/mwin/winlib/graph3d.c +WINLIB_SRCS += microwindows/src/mwin/winlib/caret.c +WINLIB_SRCS += microwindows/src/mwin/winlib/static.c +WINLIB_SRCS += microwindows/src/mwin/winlib/button.c +WINLIB_SRCS += microwindows/src/mwin/winlib/newedit.c +WINLIB_SRCS += microwindows/src/mwin/winlib/newlistbox.c +WINLIB_SRCS += microwindows/src/mwin/winlib/combobox.c +WINLIB_SRCS += microwindows/src/mwin/winlib/progbar.c +WINLIB_SRCS += microwindows/src/mwin/winlib/scrlbar.c +WINLIB_SRCS += microwindows/src/mwin/winlib/medit.c +WINLIB_SRCS += microwindows/src/mwin/winlib/ptinsid.c +WINLIB_SRCS += microwindows/src/mwin/winlib/insetr.c + +CSRCS += $(MWIN_CORE_SRCS) $(WINLIB_SRCS) +CFLAGS += -DNOGDI + +endif + +#$(error EXTOBJS=$(EXTOBJS)) + +include $(APPDIR)/Application.mk + +ifeq ($(wildcard $(MICROWINDOWS_UNPACKNAME)/.git),) +distclean:: + $(call DELDIR, $(MICROWINDOWS_UNPACKNAME)) + $(call DELFILE, $(MICROWINDOWS_TARBALL)) +endif diff --git a/graphics/microwindows/mwin-local/kbd_nuttx.c b/graphics/microwindows/mwin-local/kbd_nuttx.c new file mode 100644 index 00000000000..6f139eeeb2f --- /dev/null +++ b/graphics/microwindows/mwin-local/kbd_nuttx.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include "device.h" +#include "mwtypes.h" + +enum kbd_mode_e + { + KBD_MODE_NONE = 0, + KBD_MODE_EVENT = 1, + KBD_MODE_RAW = 2, + }; + +static int kbd_fd = -1; +static enum kbd_mode_e g_kbd_mode = KBD_MODE_NONE; +static MWKEYMOD modifiers = 0; + +static bool g_pending_release = false; +static uint8_t g_last_raw_key = 0; + +/* Standard keyboard_event_s paths, eg. sim */ +static const char *g_kbd_event_paths[] = { + "/dev/kbd", + "/dev/kbd0", + "/dev/kbd1", + "/dev/ukeyboard", + NULL +}; + +/* Legacy raw character paths, eg. qemu */ +static const char *g_kbd_raw_paths[] = { + "/dev/kbda", + "/dev/kbdb", + NULL +}; + +static int nuttxkbd_Open(KBDDEVICE * pkd); +static void nuttxkbd_Close(void); +static void nuttxkbd_GetModifierInfo(MWKEYMOD * mods, MWKEYMOD * curmods); +static int nuttxkbd_Read(MWKEY * kbuf, MWKEYMOD * mods, MWSCANCODE * scancode); + +KBDDEVICE kbddev = { + nuttxkbd_Open, + nuttxkbd_Close, + nuttxkbd_GetModifierInfo, + nuttxkbd_Read, + NULL +}; + +static MWKEY translate_keycode(uint32_t code) +{ + switch (code) + { + case KEYCODE_BACKDEL: + return MWKEY_BACKSPACE; + case KEYCODE_FWDDEL: + return MWKEY_DELETE; + case KEYCODE_HOME: + return MWKEY_HOME; + case KEYCODE_END: + return MWKEY_END; + case KEYCODE_LEFT: + return MWKEY_LEFT; + case KEYCODE_RIGHT: + return MWKEY_RIGHT; + case KEYCODE_UP: + return MWKEY_UP; + case KEYCODE_DOWN: + return MWKEY_DOWN; + case KEYCODE_PAGEUP: + return MWKEY_PAGEUP; + case KEYCODE_PAGEDOWN: + return MWKEY_PAGEDOWN; + case KEYCODE_INSERT: + return MWKEY_INSERT; + case KEYCODE_ENTER: + return MWKEY_ENTER; + case KEYCODE_F1: + return MWKEY_F1; + case KEYCODE_F2: + return MWKEY_F2; + case KEYCODE_F3: + return MWKEY_F3; + case KEYCODE_F4: + return MWKEY_F4; + case KEYCODE_F5: + return MWKEY_F5; + case KEYCODE_F6: + return MWKEY_F6; + case KEYCODE_F7: + return MWKEY_F7; + case KEYCODE_F8: + return MWKEY_F8; + case KEYCODE_F9: + return MWKEY_F9; + case KEYCODE_F10: + return MWKEY_F10; + case KEYCODE_F11: + return MWKEY_F11; + case KEYCODE_F12: + return MWKEY_F12; + case KEYCODE_CAPSLOCK: + return MWKEY_CAPSLOCK; + case KEYCODE_NUMLOCK: + return MWKEY_NUMLOCK; + case KEYCODE_SCROLLLOCK: + return MWKEY_SCROLLOCK; + case KEYCODE_PAUSE: + return MWKEY_PAUSE; + case KEYCODE_PRTSCRN: + return MWKEY_PRINT; + case KEYCODE_MENU: + return MWKEY_MENU; + + default: + if (code < 128) + { + return (MWKEY) code; + } + return MWKEY_UNKNOWN; + } +} + +static void update_modifiers(MWKEY key, int press) +{ + MWKEYMOD mask = 0; + + switch (key) + { + case MWKEY_LSHIFT: + mask = MWKMOD_LSHIFT; + break; + case MWKEY_RSHIFT: + mask = MWKMOD_RSHIFT; + break; + case MWKEY_LCTRL: + mask = MWKMOD_LCTRL; + break; + case MWKEY_RCTRL: + mask = MWKMOD_RCTRL; + break; + case MWKEY_LALT: + mask = MWKMOD_LALT; + break; + case MWKEY_RALT: + mask = MWKMOD_RALT; + break; + case MWKEY_LMETA: + mask = MWKMOD_LMETA; + break; + case MWKEY_RMETA: + mask = MWKMOD_RMETA; + break; + case MWKEY_NUMLOCK: + mask = MWKMOD_NUM; + break; + case MWKEY_CAPSLOCK: + mask = MWKMOD_CAPS; + break; + } + + if (mask) + { + if (press) + { + modifiers |= mask; + } + else + { + modifiers &= ~mask; + } + } +} + +static MWKEY raw_byte_to_mwkey(uint8_t ch) +{ + if (ch < 128) + { + return (MWKEY) ch; + } + return MWKEY_UNKNOWN; +} + +static int nuttxkbd_Open(KBDDEVICE *pkd) +{ + int i; + useconds_t delay = 1000; + + if (kbd_fd >= 0) + { + close(kbd_fd); + kbd_fd = -1; + } + + g_kbd_mode = KBD_MODE_NONE; + g_pending_release = false; + modifiers = 0; + + /* Retry with exponential backoff to wait for asynchronously loaded input + * device drivers (e.g. USB HID) to register their device nodes. */ + while (delay <= 1024000) + { + for (i = 0; g_kbd_event_paths[i]; i++) + { + kbd_fd = open(g_kbd_event_paths[i], O_RDONLY | O_NONBLOCK); + if (kbd_fd >= 0) + { + g_kbd_mode = KBD_MODE_EVENT; + return DRIVER_OKFILEDESC(kbd_fd); + } + } + + for (i = 0; g_kbd_raw_paths[i]; i++) + { + kbd_fd = open(g_kbd_raw_paths[i], O_RDONLY | O_NONBLOCK); + if (kbd_fd >= 0) + { + g_kbd_mode = KBD_MODE_RAW; + g_pending_release = false; + return DRIVER_OKFILEDESC(kbd_fd); + } + } + + usleep(delay); + delay *= 2; + } + + return DRIVER_FAIL; +} + +static void nuttxkbd_Close(void) +{ + if (kbd_fd >= 0) + { + close(kbd_fd); + kbd_fd = -1; + } + modifiers = 0; + g_kbd_mode = KBD_MODE_NONE; + g_pending_release = false; +} + +static void nuttxkbd_GetModifierInfo(MWKEYMOD *mods, MWKEYMOD *curmods) +{ + if (mods) + { + *mods = MWKMOD_SHIFT | + MWKMOD_CTRL | MWKMOD_ALT | MWKMOD_META | MWKMOD_NUM | MWKMOD_CAPS; + } + if (curmods) + { + *curmods = modifiers; + } +} + +static int nuttxkbd_Read(MWKEY *kbuf, MWKEYMOD *mods, MWSCANCODE *scancode) +{ + if (g_kbd_mode == KBD_MODE_EVENT) + { + struct keyboard_event_s event; + int n; + + n = read(kbd_fd, &event, sizeof(event)); + if (n < (int)sizeof(event)) + { + return KBD_NODATA; + } + + MWKEY key = translate_keycode(event.code); + int press = (event.type == KEYBOARD_PRESS); + + update_modifiers(key, press); + + *kbuf = key; + *mods = modifiers; + *scancode = 0; + + return press ? KBD_KEYPRESS : KBD_KEYRELEASE; + } + else + { + uint8_t ch; + int n; + + if (g_pending_release) + { + g_pending_release = false; + *kbuf = (MWKEY) g_last_raw_key; + *mods = modifiers; + *scancode = 0; + return KBD_KEYRELEASE; + } + + n = read(kbd_fd, &ch, 1); + if (n < 1) + { + return KBD_NODATA; + } + + MWKEY key = raw_byte_to_mwkey(ch); + if (key == MWKEY_UNKNOWN) + { + return KBD_NODATA; + } + + g_pending_release = true; + g_last_raw_key = ch; + + *kbuf = key; + *mods = modifiers; + *scancode = 0; + return KBD_KEYPRESS; + } +} diff --git a/graphics/microwindows/mwin-local/mou_nuttx.c b/graphics/microwindows/mwin-local/mou_nuttx.c new file mode 100644 index 00000000000..090b97e59fc --- /dev/null +++ b/graphics/microwindows/mwin-local/mou_nuttx.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include "device.h" + +#define SCALE 3 +#define THRESH 5 + +#define TYPE_REALMOUSE 1 +#define TYPE_TOUCHSCREEN 0 +#define MOUSE_DEVICE_PATH "/dev/mouse0" +#define TOUCHSCREEN_DEVICE_PATH "/dev/input0" + +static int fd = -1; +static int mouse_type = TYPE_TOUCHSCREEN; + +static int nuttxmouse_Open(MOUSEDEVICE * pmd); +static void nuttxmouse_Close(void); +static int nuttxmouse_GetButtonInfo(void); +static void nuttxmouse_GetDefaultAccel(int *pscale, int *pthresh); +static int nuttxmouse_Read(MWCOORD * dx, MWCOORD * dy, MWCOORD * dz, int *bp); + +MOUSEDEVICE mousedev = { + nuttxmouse_Open, + nuttxmouse_Close, + nuttxmouse_GetButtonInfo, + nuttxmouse_GetDefaultAccel, + nuttxmouse_Read, + NULL, + MOUSE_NORMAL +}; + +static int nuttxmouse_Open(MOUSEDEVICE *pmd) +{ + useconds_t delay = 1000; + + /* Retry with exponential backoff to wait for asynchronously loaded input + * device drivers (e.g. USB HID) to register their device nodes. */ + while (delay <= 1024000) + { + fd = open(MOUSE_DEVICE_PATH, O_RDONLY | O_NONBLOCK); + if (fd >= 0) + { + mouse_type = TYPE_REALMOUSE; + return DRIVER_OKFILEDESC(fd); + } + + fd = open(TOUCHSCREEN_DEVICE_PATH, O_RDONLY | O_NONBLOCK); + if (fd >= 0) + { + mouse_type = TYPE_TOUCHSCREEN; + return DRIVER_OKFILEDESC(fd); + } + + usleep(delay); + delay *= 2; + } + + return MOUSE_FAIL; +} + +static void nuttxmouse_Close(void) +{ + if (fd >= 0) + { + close(fd); + fd = -1; + } +} + +static int nuttxmouse_GetButtonInfo(void) +{ + if (mouse_type == TYPE_REALMOUSE) + { + return MWBUTTON_L | MWBUTTON_M | MWBUTTON_R | MWBUTTON_SCROLLUP | + MWBUTTON_SCROLLDN; + } + else + { + return MWBUTTON_L; + } +} + +static void nuttxmouse_GetDefaultAccel(int *pscale, int *pthresh) +{ + *pscale = SCALE; + *pthresh = THRESH; +} + +static int nuttxmouse_Read(MWCOORD *dx, MWCOORD *dy, MWCOORD *dz, int *bp) +{ + if (mouse_type == TYPE_REALMOUSE) + { + struct mouse_report_s report; + int n = read(fd, &report, sizeof(report)); + if (n <= 0) + return MOUSE_NODATA; + + *dx = report.x; + *dy = report.y; + *dz = report.wheel; + + *bp = 0; + if (report.buttons & MOUSE_BUTTON_1) + *bp |= MWBUTTON_L; + if (report.buttons & MOUSE_BUTTON_2) + *bp |= MWBUTTON_R; + if (report.buttons & MOUSE_BUTTON_3) + *bp |= MWBUTTON_M; + + if (report.wheel > 0) + *bp |= MWBUTTON_SCROLLUP; + if (report.wheel < 0) + *bp |= MWBUTTON_SCROLLDN; + + return MOUSE_ABSPOS; + } + else + { + struct touch_sample_s sample; + int n = read(fd, &sample, sizeof(sample)); + if (n <= 0) + return MOUSE_NODATA; + + *dx = sample.point[0].x; + *dy = sample.point[0].y; + *dz = 0; + + uint8_t flags = sample.point[0].flags; + if (flags & (TOUCH_DOWN | TOUCH_MOVE)) + { + *bp = MWBUTTON_L; + } + else + { + *bp = 0; + } + return MOUSE_ABSPOS; + } +} diff --git a/graphics/microwindows/mwin-local/scr_nuttx.c b/graphics/microwindows/mwin-local/scr_nuttx.c new file mode 100644 index 00000000000..e77dbdaa244 --- /dev/null +++ b/graphics/microwindows/mwin-local/scr_nuttx.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include "device.h" +#include "genfont.h" +#include "genmem.h" +#include "fb.h" + +static int fb = -1; + +static PSD fb_open(PSD psd); +static void fb_close(PSD psd); + +SCREENDEVICE scrdev = { + 0, 0, 0, 0, 0, 0, 0, NULL, 0, NULL, 0, 0, 0, 0, 0, 0, + gen_fonts, + fb_open, + fb_close, + NULL, + gen_getscreeninfo, + gen_allocatememgc, + gen_mapmemgc, + gen_freememgc, + gen_setportrait, + NULL, + NULL +}; + +static PSD fb_open(PSD psd) +{ + struct fb_videoinfo_s vinfo; + struct fb_planeinfo_s pinfo; + PSUBDRIVER subdriver; + + fb = open("/dev/fb0", O_RDWR); + if (fb < 0) + { + EPRINTF("Error: cannot open /dev/fb0. Check CONFIG_VIDEO_FB\n"); + return NULL; + } + + if (ioctl(fb, FBIOGET_VIDEOINFO, &vinfo) < 0 || + ioctl(fb, FBIOGET_PLANEINFO, &pinfo) < 0) + { + EPRINTF("Error: cannot read framebuffer info\n"); + goto fail; + } + + if (vinfo.xres == 0 || vinfo.yres == 0) + { + EPRINTF("Error: zero resolution\n"); + goto fail; + } + + psd->xres = psd->xvirtres = vinfo.xres; + psd->yres = psd->yvirtres = vinfo.yres; + psd->bpp = pinfo.bpp; + psd->planes = 1; + psd->pitch = pinfo.stride; + psd->size = pinfo.fblen; + psd->ncolors = (psd->bpp >= 24) ? (1 << 24) : (1 << psd->bpp); + psd->flags = PSF_SCREEN | PSF_ADDRMMAP; + psd->portrait = MWPORTRAIT_NONE; + + psd->pixtype = SCREEN_PIXTYPE; + psd->data_format = set_data_format(psd); + + psd->addr = mmap(NULL, psd->size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0); + if (psd->addr == MAP_FAILED) + { + EPRINTF("Error: mmap failed\n"); + goto fail; + } + + subdriver = select_fb_subdriver(psd); + if (!subdriver) + { + EPRINTF("Error: no subdriver for %dbpp\n", psd->bpp); + goto fail; + } + set_subdriver(psd, subdriver); + + return psd; + +fail: + if (fb >= 0) + { + close(fb); + fb = -1; + } + return NULL; +} + +static void fb_close(PSD psd) +{ + if (fb >= 0) + { + munmap(psd->addr, psd->size); + close(fb); + fb = -1; + } +}