This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git

commit d79e503612c7d25a606f2f8b3d7fc1cd6f5a85b3
Author: Acfboy <[email protected]>
AuthorDate: Sat Aug 1 23:00:02 2026 +0800

    examples/nanoxcalc: add Nano-X calculator example
    
    * Port the nxcalc demo from Microwindows
    * Connects to the Nano-X server started by examples/nanoxterm
    * Requires CONFIG_LIBC_FLOATINGPOINT for the result formatting
    
    Signed-off-by: Acfboy <[email protected]>
---
 examples/nanoxcalc/Kconfig   |  28 +++
 examples/nanoxcalc/Make.defs |  25 +++
 examples/nanoxcalc/Makefile  |  35 +++
 examples/nanoxcalc/nxcalc.c  | 510 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 598 insertions(+)

diff --git a/examples/nanoxcalc/Kconfig b/examples/nanoxcalc/Kconfig
new file mode 100644
index 000000000..5b2fa079c
--- /dev/null
+++ b/examples/nanoxcalc/Kconfig
@@ -0,0 +1,28 @@
+config EXAMPLES_NANOXCALC
+       tristate "Nano-X calculator example"
+       default n
+       depends on MICROWINDOWS_NANOX && LIBC_FLOATINGPOINT
+       ---help---
+               Enable the Nano-X calculator example application.  It is a
+               port of the nxcalc demo from Microwindows which connects to
+               the Nano-X server started by examples/nanoxterm and shows
+               a calculator keypad operated by the mouse or keyboard.
+
+if EXAMPLES_NANOXCALC
+
+config EXAMPLES_NANOXCALC_PROGNAME
+       string "Program name"
+       default "nanoxcalc"
+       ---help---
+               This is the name of the program that will be used when the NSH 
ELF
+               program is installed.
+
+config EXAMPLES_NANOXCALC_PRIORITY
+       int "Nano-X calculator task priority"
+       default 100
+
+config EXAMPLES_NANOXCALC_STACKSIZE
+       int "Nano-X calculator stack size"
+       default 16384
+
+endif
diff --git a/examples/nanoxcalc/Make.defs b/examples/nanoxcalc/Make.defs
new file mode 100644
index 000000000..c30810158
--- /dev/null
+++ b/examples/nanoxcalc/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/examples/nanoxcalc/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_NANOXCALC),)
+CONFIGURED_APPS += $(APPDIR)/examples/nanoxcalc
+endif
diff --git a/examples/nanoxcalc/Makefile b/examples/nanoxcalc/Makefile
new file mode 100644
index 000000000..7a703a398
--- /dev/null
+++ b/examples/nanoxcalc/Makefile
@@ -0,0 +1,35 @@
+############################################################################
+# apps/examples/nanoxcalc/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_NANOXCALC_PROGNAME)
+PRIORITY  = $(CONFIG_EXAMPLES_NANOXCALC_PRIORITY)
+STACKSIZE = $(CONFIG_EXAMPLES_NANOXCALC_STACKSIZE)
+MODULE    = $(CONFIG_EXAMPLES_NANOXCALC)
+
+# Port of the upstream nxcalc demo (src/demos/nanox/nxcalc.c)
+MAINSRC = nxcalc.c
+CFLAGS += -Wno-undef
+
+
+include $(APPDIR)/Application.mk
diff --git a/examples/nanoxcalc/nxcalc.c b/examples/nanoxcalc/nxcalc.c
new file mode 100644
index 000000000..8a7936dcf
--- /dev/null
+++ b/examples/nanoxcalc/nxcalc.c
@@ -0,0 +1,510 @@
+/****************************************************************************
+ * apps/examples/nanoxcalc/nxcalc.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * nxcalc - Simple graphical calculator for Microwindows / Nano-X
+ *
+ * Inspired by the nxclock example (Greg Haerr).
+ *
+ * Run inside an environment where the Nano-X server is available.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "nano-X.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+#define WIN_W   160
+#define WIN_H   200
+#define DISP_H  35
+#define COLS    4
+#define ROWS    5
+static GR_WINDOW_ID win;
+static GR_GC_ID gc_text;
+static GR_GC_ID gc_button;
+static GR_GC_ID gc_button_press;
+static GR_GC_ID gc_border;
+
+/* calculator state */
+
+static char display[128];
+static double acc = 0.0;
+static char pending_op = 0;
+static int entering = 0;
+static int dot_entered = 0;
+
+/* button layout */
+
+static char *btn_labels[] =
+{
+  "C", "<-", "+/-", "/", "7", "8", "9", "*",
+  "4", "5", "6", "-", "1", "2", "3", "+",
+  "0", ".", "=", NULL
+};
+
+static GR_RECT btn_rects[ROWS][COLS];
+
+/* function prototypes */
+
+static void redraw(void);
+static void press_button(const char *label);
+static void compute_equal(void);
+static void apply_pending(double val);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static double display_to_double(void)
+{
+  if (strlen(display) == 0)
+    {
+      return 0.0;
+    }
+
+  return strtod(display, NULL);
+}
+
+static void set_display_from_double(double v)
+{
+  char buf[128];
+  double iv;
+
+  if (fabs(v) < 1e-12)
+    {
+      v = 0.0;
+    }
+
+  if (modf(v, &iv) == 0.0)
+    {
+      snprintf(buf, sizeof(buf), "%.0f", v);
+    }
+  else
+    {
+      snprintf(buf, sizeof(buf), "%.10g", v);
+    }
+
+  strncpy(display, buf, sizeof(display) - 1);
+  display[sizeof(display) - 1] = '\0';
+}
+
+/* ========================= MAIN ========================= */
+
+int main(int argc, char **argv)
+{
+  GR_EVENT event;
+  int r;
+  int c;
+  int idx;
+
+  if (GrOpen() < 0)
+    {
+      fprintf(stderr, "nxcalc: cannot open Nano-X\n");
+      return 1;
+    }
+
+  win = GrNewWindowEx(GR_WM_PROPS_APPWINDOW, "nxcalc", GR_ROOT_WINDOW_ID, 10,
+    10, WIN_W, WIN_H, GrGetSysColor(GR_COLOR_WINDOW));
+
+  GrSelectEvents(win,
+    GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_BUTTON_DOWN |
+    GR_EVENT_MASK_BUTTON_UP | GR_EVENT_MASK_CLOSE_REQ);
+
+  gc_text = GrNewGC();
+  gc_button = GrNewGC();
+  gc_button_press = GrNewGC();
+  gc_border = GrNewGC();
+
+  GrSetGCForeground(gc_text, GrGetSysColor(GR_COLOR_WINDOWTEXT));
+  GrSetGCBackground(gc_text, GrGetSysColor(GR_COLOR_WINDOW));
+
+  GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_BTNFACE));
+  GrSetGCBackground(gc_button, GrGetSysColor(GR_COLOR_WINDOW));
+
+  GrSetGCForeground(gc_button_press, GrGetSysColor(GR_COLOR_BTNSHADOW));
+  GrSetGCBackground(gc_button_press, GrGetSysColor(GR_COLOR_WINDOW));
+
+  GrSetGCForeground(gc_border, GrGetSysColor(GR_COLOR_BTNTEXT));
+  GrSetGCBackground(gc_border, GrGetSysColor(GR_COLOR_WINDOW));
+
+  /* init state */
+
+  display[0] = '\0';
+  acc = 0.0;
+  pending_op = 0;
+  entering = 0;
+  dot_entered = 0;
+
+  /* layout buttons */
+
+  int grid_x = 12;
+  int grid_y = DISP_H + 10;
+  int grid_w = WIN_W - 20;
+  int grid_h = WIN_H - DISP_H - 20;
+  int cell_w = grid_w / COLS;
+  int cell_h = grid_h / ROWS;
+
+  idx = 0;
+  for (r = 0; r < ROWS; r++)
+    {
+      for (c = 0; c < COLS; c++)
+        {
+          int x = grid_x + c * cell_w;
+          int y = grid_y + r * cell_h;
+          btn_rects[r][c].x = x;
+          btn_rects[r][c].y = y;
+          btn_rects[r][c].width = cell_w - 6;
+          btn_rects[r][c].height = cell_h - 6;
+          idx++;
+        }
+    }
+
+  GrMapWindow(win);
+  set_display_from_double(0.0);
+  redraw();
+
+  while (1)
+    {
+      GrGetNextEvent(&event);
+      switch (event.type)
+        {
+          case GR_EVENT_TYPE_EXPOSURE:
+            {
+              redraw();
+              break;
+            }
+
+          case GR_EVENT_TYPE_BUTTON_DOWN:
+            {
+              int mx = event.button.x;
+              int my = event.button.y;
+              int found = 0;
+              for (r = 0; r < ROWS && !found; r++)
+                {
+                  for (c = 0; c < COLS && !found; c++)
+                    {
+                      GR_RECT *rc = &btn_rects[r][c];
+                      if (mx >= rc->x && mx <= rc->x + rc->width &&
+                          my >= rc->y && my <= rc->y + rc->height)
+                        {
+                          GrFillRect(win, gc_button_press, rc->x, rc->y,
+                            rc->width, rc->height);
+                          found = 1;
+                        }
+                    }
+                }
+              break;
+            }
+
+          case GR_EVENT_TYPE_BUTTON_UP:
+            {
+              int mx = event.button.x;
+              int my = event.button.y;
+              int found = 0;
+              for (r = 0; r < ROWS && !found; r++)
+                {
+                  for (c = 0; c < COLS && !found; c++)
+                    {
+                      GR_RECT *rc = &btn_rects[r][c];
+                      if (mx >= rc->x && mx <= rc->x + rc->width &&
+                          my >= rc->y && my <= rc->y + rc->height)
+                        {
+                          idx = r * COLS + c;
+                          const char *lbl = (idx <
+                                             (int)(sizeof(btn_labels) /
+                                                   sizeof(btn_labels[0]))?
+                                             btn_labels[idx] : NULL);
+                          if (lbl)
+                            {
+                              press_button(lbl);
+                              redraw();
+                            }
+
+                          found = 1;
+                        }
+                    }
+                }
+              break;
+            }
+
+          case GR_EVENT_TYPE_CLOSE_REQ:
+            {
+              GrClose();
+              return 0;
+            }
+        }
+    }
+
+  return 0;
+}
+
+/* ========================= UI DRAWING ========================= */
+
+static void redraw(void)
+{
+  int r;
+  int c;
+  int idx;
+  int tw;
+  int th;
+  int tb;
+
+  /* display area */
+
+  GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_WINDOW));
+  GrFillRect(win, gc_button, 5, 5, WIN_W - 10, DISP_H - 10);
+  GrSetGCForeground(gc_border, GrGetSysColor(GR_COLOR_BTNSHADOW));
+  GrRect(win, gc_border, 5, 5, WIN_W - 11, DISP_H - 10);
+
+  /* display text (right aligned) */
+
+  GrGetGCTextSize(gc_text, display, strlen(display), GR_TFTOP, &tw,
+                   &th, &tb);
+
+  int x = WIN_W - 12 - tw;
+  if (x < 10)
+    {
+      x = 10;
+    }
+
+    GrText(win, gc_text, x, 5 + (DISP_H / 2) - 10, display, strlen(display),
+      GR_TFTOP);
+
+  /* draw buttons */
+
+  idx = 0;
+  GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_BTNFACE));
+  for (r = 0; r < ROWS; r++)
+    {
+      for (c = 0; c < COLS; c++)
+        {
+          GR_RECT *rc = &btn_rects[r][c];
+          char *lbl = (idx <
+                       (int)(sizeof(btn_labels) /
+                             sizeof(btn_labels[0]))? btn_labels[idx] : NULL);
+
+          GrSetGCForeground(gc_button, GrGetSysColor(GR_COLOR_WINDOW));
+          GrFillRect(win, gc_button, rc->x, rc->y, rc->width, rc->height);
+          GrSetGCForeground(gc_border, GrGetSysColor(GR_COLOR_BTNSHADOW));
+          GrRect(win, gc_border, rc->x, rc->y, rc->width, rc->height);
+
+          if (lbl)
+            {
+              GrGetGCTextSize(gc_text, lbl, strlen(lbl), GR_TFTOP, &tw, &th,
+                &tb);
+              GrText(win, gc_text, rc->x + rc->width / 2 - tw / 2,
+                rc->y + rc->height / 2 - 6, lbl, strlen(lbl), GR_TFTOP);
+            }
+
+          idx++;
+        }
+    }
+
+  GrFlush();
+}
+
+/* ========================= LOGIC ========================= */
+
+static void press_button(const char *label)
+{
+  char tmp[128];
+  double val;
+
+  if (strcmp(label, "C") == 0)
+    {
+      display[0] = '\0';
+      acc = 0.0;
+      pending_op = 0;
+      entering = 0;
+      dot_entered = 0;
+      set_display_from_double(0.0);
+      return;
+    }
+
+  if (strcmp(label, "<-") == 0)
+    {
+      int l = strlen(display);
+      if (l > 0)
+        {
+          if (display[l - 1] == '.')
+            {
+              dot_entered = 0;
+            }
+
+          display[l - 1] = '\0';
+          if (strlen(display) == 0)
+            {
+              set_display_from_double(0.0);
+            }
+        }
+
+      entering = (strlen(display) > 0);
+      return;
+    }
+
+  if (strcmp(label, "+/-") == 0)
+    {
+      if (display[0] == '-')
+        {
+          memmove(display, display + 1, strlen(display));
+        }
+      else
+        {
+          if (strlen(display) == 0 || strcmp(display, "0") == 0)
+            {
+              return;
+            }
+
+          snprintf(tmp, sizeof(tmp), "-%s", display);
+          strncpy(display, tmp, sizeof(display) - 1);
+        }
+
+      return;
+    }
+
+  if (strcmp(label, "+") == 0 || strcmp(label, "-") == 0 || strcmp(label,
+    "*") == 0 || strcmp(label, "/") == 0)
+    {
+      val = display_to_double();
+      if (pending_op == 0)
+        {
+          acc = val;
+        }
+      else
+        {
+          apply_pending(val);
+        }
+
+      pending_op = label[0];
+      entering = 0;
+      dot_entered = 0;
+      set_display_from_double(acc);
+      return;
+    }
+
+  if (strcmp(label, "=") == 0)
+    {
+      compute_equal();
+      return;
+    }
+
+  if (strcmp(label, ".") == 0)
+    {
+      if (!dot_entered)
+        {
+          if (!entering)
+            {
+              strcpy(display, "0.");
+              entering = 1;
+            }
+          else
+            {
+              strncat(display, ".", sizeof(display) - strlen(display) - 1);
+            }
+
+          dot_entered = 1;
+        }
+
+      return;
+    }
+
+  if (strlen(label) == 1 && isdigit((unsigned char)label[0]))
+    {
+      if (!entering)
+        {
+          display[0] = label[0];
+          display[1] = '\0';
+          entering = 1;
+        }
+      else
+        {
+          if (strcmp(display, "0") == 0 && label[0] != '0' && !dot_entered)
+            {
+              display[0] = label[0];
+              display[1] = '\0';
+            }
+          else if (strlen(display) < sizeof(display) - 2)
+            {
+              strncat(display, label, 1);
+            }
+        }
+
+      return;
+    }
+}
+
+static void apply_pending(double val)
+{
+  if (pending_op == '+')
+    {
+      acc = acc + val;
+    }
+  else if (pending_op == '-')
+    {
+      acc = acc - val;
+    }
+  else if (pending_op == '*')
+    {
+      acc = acc * val;
+    }
+  else if (pending_op == '/')
+    {
+      if (val == 0.0)
+        {
+          strncpy(display, "Error", sizeof(display) - 1);
+          display[sizeof(display) - 1] = '\0';
+          pending_op = 0;
+          entering = 0;
+          dot_entered = 0;
+          redraw();
+          return;
+        }
+      else
+        {
+          acc = acc / val;
+        }
+    }
+}
+
+static void compute_equal(void)
+{
+  double val = display_to_double();
+
+  if (pending_op != 0)
+    {
+      apply_pending(val);
+      pending_op = 0;
+      set_display_from_double(acc);
+      entering = 0;
+      dot_entered = 0;
+    }
+  else
+    {
+      set_display_from_double(val);
+    }
+}

Reply via email to