mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-23 06:29:10 +00:00
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 <AcfboyU@outlook.com>
This commit is contained in:
parent
edb4d26663
commit
d79e503612
4 changed files with 598 additions and 0 deletions
28
examples/nanoxcalc/Kconfig
Normal file
28
examples/nanoxcalc/Kconfig
Normal file
|
|
@ -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
|
||||
25
examples/nanoxcalc/Make.defs
Normal file
25
examples/nanoxcalc/Make.defs
Normal file
|
|
@ -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
|
||||
35
examples/nanoxcalc/Makefile
Normal file
35
examples/nanoxcalc/Makefile
Normal file
|
|
@ -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
|
||||
510
examples/nanoxcalc/nxcalc.c
Normal file
510
examples/nanoxcalc/nxcalc.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue