1194 lines
28 KiB
C
1194 lines
28 KiB
C
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2023-2024 erysdren (it/she/they)
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* EUI.C
|
|
*
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
|
|
#include "eui.h"
|
|
|
|
/*
|
|
*
|
|
* font8x8
|
|
*
|
|
*/
|
|
|
|
static const unsigned char font8x8_basic[128][8] = {
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0000 (nul) */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0001 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0002 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0003 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0004 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0005 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0006 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0007 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0008 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0009 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+000A */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+000B */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+000C */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+000D */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+000E */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+000F */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0010 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0011 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0012 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0013 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0014 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0015 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0016 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0017 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0018 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0019 */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+001A */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+001B */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+001C */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+001D */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+001E */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+001F */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0020 (space) */
|
|
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00 }, /* U+0021 (!) */
|
|
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0022 (") */
|
|
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00 }, /* U+0023 (#) */
|
|
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00 }, /* U+0024 ($) */
|
|
{ 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00 }, /* U+0025 (%) */
|
|
{ 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00 }, /* U+0026 (&) */
|
|
{ 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0027 (') */
|
|
{ 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00 }, /* U+0028 (() */
|
|
{ 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00 }, /* U+0029 ()) */
|
|
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00 }, /* U+002A (*) */
|
|
{ 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00 }, /* U+002B (+) */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, /* U+002C (,) */
|
|
{ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00 }, /* U+002D (-) */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, /* U+002E (.) */
|
|
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00 }, /* U+002F (/) */
|
|
{ 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00 }, /* U+0030 (0) */
|
|
{ 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00 }, /* U+0031 (1) */
|
|
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00 }, /* U+0032 (2) */
|
|
{ 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00 }, /* U+0033 (3) */
|
|
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00 }, /* U+0034 (4) */
|
|
{ 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00 }, /* U+0035 (5) */
|
|
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00 }, /* U+0036 (6) */
|
|
{ 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 }, /* U+0037 (7) */
|
|
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00 }, /* U+0038 (8) */
|
|
{ 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00 }, /* U+0039 (9) */
|
|
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00 }, /* U+003A (:) */
|
|
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06 }, /* U+003B (;) */
|
|
{ 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00 }, /* U+003C (<) */
|
|
{ 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00 }, /* U+003D (=) */
|
|
{ 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00 }, /* U+003E (>) */
|
|
{ 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00 }, /* U+003F (?) */
|
|
{ 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00 }, /* U+0040 (@) */
|
|
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00 }, /* U+0041 (A) */
|
|
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00 }, /* U+0042 (B) */
|
|
{ 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00 }, /* U+0043 (C) */
|
|
{ 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00 }, /* U+0044 (D) */
|
|
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00 }, /* U+0045 (E) */
|
|
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00 }, /* U+0046 (F) */
|
|
{ 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00 }, /* U+0047 (G) */
|
|
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00 }, /* U+0048 (H) */
|
|
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, /* U+0049 (I) */
|
|
{ 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00 }, /* U+004A (J) */
|
|
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00 }, /* U+004B (K) */
|
|
{ 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00 }, /* U+004C (L) */
|
|
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00 }, /* U+004D (M) */
|
|
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00 }, /* U+004E (N) */
|
|
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00 }, /* U+004F (O) */
|
|
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00 }, /* U+0050 (P) */
|
|
{ 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00 }, /* U+0051 (Q) */
|
|
{ 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00 }, /* U+0052 (R) */
|
|
{ 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00 }, /* U+0053 (S) */
|
|
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, /* U+0054 (T) */
|
|
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00 }, /* U+0055 (U) */
|
|
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, /* U+0056 (V) */
|
|
{ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00 }, /* U+0057 (W) */
|
|
{ 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00 }, /* U+0058 (X) */
|
|
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00 }, /* U+0059 (Y) */
|
|
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00 }, /* U+005A (Z) */
|
|
{ 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00 }, /* U+005B ([) */
|
|
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00 }, /* U+005C (\) */
|
|
{ 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00 }, /* U+005D (]) */
|
|
{ 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00 }, /* U+005E (^) */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, /* U+005F (_) */
|
|
{ 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+0060 (`) */
|
|
{ 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00 }, /* U+0061 (a) */
|
|
{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00 }, /* U+0062 (b) */
|
|
{ 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00 }, /* U+0063 (c) */
|
|
{ 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00 }, /* U+0064 (d) */
|
|
{ 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00 }, /* U+0065 (e) */
|
|
{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00 }, /* U+0066 (f) */
|
|
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F }, /* U+0067 (g) */
|
|
{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00 }, /* U+0068 (h) */
|
|
{ 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, /* U+0069 (i) */
|
|
{ 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E }, /* U+006A (j) */
|
|
{ 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00 }, /* U+006B (k) */
|
|
{ 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00 }, /* U+006C (l) */
|
|
{ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00 }, /* U+006D (m) */
|
|
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00 }, /* U+006E (n) */
|
|
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00 }, /* U+006F (o) */
|
|
{ 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F }, /* U+0070 (p) */
|
|
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78 }, /* U+0071 (q) */
|
|
{ 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00 }, /* U+0072 (r) */
|
|
{ 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00 }, /* U+0073 (s) */
|
|
{ 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00 }, /* U+0074 (t) */
|
|
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00 }, /* U+0075 (u) */
|
|
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00 }, /* U+0076 (v) */
|
|
{ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00 }, /* U+0077 (w) */
|
|
{ 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00 }, /* U+0078 (x) */
|
|
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F }, /* U+0079 (y) */
|
|
{ 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00 }, /* U+007A (z) */
|
|
{ 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00 }, /* U+007B ({) */
|
|
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00 }, /* U+007C (|) */
|
|
{ 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00 }, /* U+007D (}) */
|
|
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* U+007E (~) */
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } /* U+007F */
|
|
};
|
|
|
|
/*
|
|
*
|
|
* xbm icons
|
|
*
|
|
*/
|
|
|
|
/* checkbox border */
|
|
static const int checkbox_border_w = 11;
|
|
static const int checkbox_border_h = 11;
|
|
static unsigned char checkbox_border_bits[] = {
|
|
0xff, 0x07, 0x01, 0x04, 0x01, 0x04, 0x01, 0x04, 0x01, 0x04, 0x01, 0x04,
|
|
0x01, 0x04, 0x01, 0x04, 0x01, 0x04, 0x01, 0x04, 0xff, 0x07
|
|
};
|
|
|
|
/* checkbox x */
|
|
static const int checkbox_x_w = 11;
|
|
static const int checkbox_x_h = 11;
|
|
static unsigned char checkbox_x_bits[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x88, 0x00, 0x50, 0x00, 0x20, 0x00,
|
|
0x50, 0x00, 0x88, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
/*
|
|
*
|
|
* local types
|
|
*
|
|
*/
|
|
|
|
/* frame */
|
|
typedef struct frame_t {
|
|
eui_vec2_t pos;
|
|
eui_vec2_t size;
|
|
eui_vec2_t align;
|
|
} frame_t;
|
|
|
|
/*
|
|
*
|
|
* limits
|
|
*
|
|
*/
|
|
|
|
#define MAX_WIDTH (2048)
|
|
#define MAX_HEIGHT (2048)
|
|
|
|
/*
|
|
*
|
|
* local variables
|
|
*
|
|
*/
|
|
|
|
/* misc */
|
|
#ifndef sgn
|
|
#define sgn(x) ((x < 0) ? -1 : ((x > 0) ? 1 : 0))
|
|
#endif
|
|
|
|
/* frames */
|
|
#define MAX_FRAMES (64)
|
|
static frame_t frames[MAX_FRAMES] = {0};
|
|
static int frame_index = 0;
|
|
|
|
/* destination pixelmap */
|
|
static eui_pixelmap_t drawdest = {0};
|
|
#define PIXEL(pm, x, y) (pm).pixels[(y) * (pm).pitch + (x)]
|
|
|
|
/* event handling */
|
|
#define MAX_EVENTS (32)
|
|
static eui_event_t events[MAX_EVENTS] = {0};
|
|
static int num_events = 0;
|
|
|
|
/* input state */
|
|
static eui_vec2_t mouse = {0};
|
|
static int button = 0;
|
|
|
|
/* keyboard state */
|
|
#define MAX_KEYS (256)
|
|
#define KEY_BUFFER_SIZE (64)
|
|
#define KEY_BUFFER_ADVANCE(x) ((x) = ((x) + 1) & (KEY_BUFFER_SIZE - 1))
|
|
static char keys[MAX_KEYS] = {0};
|
|
static int key_buffer[KEY_BUFFER_SIZE] = {0};
|
|
static int key_buffer_ridx = 0;
|
|
static int key_buffer_widx = 0;
|
|
|
|
/* configuration state */
|
|
#if (EUI_PIXEL_DEPTH == 8)
|
|
/* assumes vga palette */
|
|
static eui_config_t config = {
|
|
/* button */
|
|
{0, 15, 15, 0, 0, 15, 1}
|
|
};
|
|
#elif (EUI_PIXEL_DEPTH == 16)
|
|
static eui_config_t config = {
|
|
/* button */
|
|
{0x0000, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 1}
|
|
};
|
|
#elif (EUI_PIXEL_DEPTH == 32)
|
|
static eui_config_t config = {
|
|
/* button */
|
|
{0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 1}
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
*
|
|
* local functions
|
|
*
|
|
*/
|
|
|
|
/* draw font8x8 bitmap at pos */
|
|
static void eui_font8x8(eui_vec2_t pos, const unsigned char *bitmap, eui_color_t color)
|
|
{
|
|
int x, y;
|
|
int xx, yy;
|
|
|
|
for (x = 0; x < 8; x++)
|
|
{
|
|
for (y = 0; y < 8; y++)
|
|
{
|
|
if (bitmap[x] & 1 << y)
|
|
{
|
|
xx = pos.x + y;
|
|
yy = pos.y + x;
|
|
|
|
if (xx < 0 || xx >= drawdest.w || yy < 0 || yy >= drawdest.h)
|
|
continue;
|
|
|
|
PIXEL(drawdest, xx, yy) = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* basic transforms
|
|
*
|
|
*/
|
|
|
|
/* transform point to current frame, with alignment */
|
|
void eui_transform_point(eui_vec2_t *pos)
|
|
{
|
|
switch (frames[frame_index].align.x)
|
|
{
|
|
case EUI_ALIGN_START:
|
|
pos->x += frames[frame_index].pos.x;
|
|
break;
|
|
case EUI_ALIGN_MIDDLE:
|
|
pos->x += frames[frame_index].pos.x + frames[frame_index].size.x / 2;
|
|
break;
|
|
case EUI_ALIGN_END:
|
|
pos->x += frames[frame_index].pos.x + frames[frame_index].size.x;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (frames[frame_index].align.y)
|
|
{
|
|
case EUI_ALIGN_START:
|
|
pos->y += frames[frame_index].pos.y;
|
|
break;
|
|
case EUI_ALIGN_MIDDLE:
|
|
pos->y += frames[frame_index].pos.y + frames[frame_index].size.y / 2;
|
|
break;
|
|
case EUI_ALIGN_END:
|
|
pos->y += frames[frame_index].pos.y + frames[frame_index].size.y;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* transform box to current frame, with alignment */
|
|
void eui_transform_box(eui_vec2_t *pos, eui_vec2_t size)
|
|
{
|
|
switch (frames[frame_index].align.x)
|
|
{
|
|
case EUI_ALIGN_START:
|
|
pos->x += frames[frame_index].pos.x;
|
|
break;
|
|
case EUI_ALIGN_MIDDLE:
|
|
pos->x += frames[frame_index].pos.x + frames[frame_index].size.x / 2 - size.x / 2;
|
|
break;
|
|
case EUI_ALIGN_END:
|
|
pos->x += frames[frame_index].pos.x + frames[frame_index].size.x - size.x;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (frames[frame_index].align.y)
|
|
{
|
|
case EUI_ALIGN_START:
|
|
pos->y += frames[frame_index].pos.y;
|
|
break;
|
|
case EUI_ALIGN_MIDDLE:
|
|
pos->y += frames[frame_index].pos.y + frames[frame_index].size.y / 2 - size.y / 2;
|
|
break;
|
|
case EUI_ALIGN_END:
|
|
pos->y += frames[frame_index].pos.y + frames[frame_index].size.y - size.y;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* clip box to screen size, returns 0 if the shape will never be visible */
|
|
int eui_clip_box(eui_vec2_t *pos, eui_vec2_t *size)
|
|
{
|
|
/* it will never become visible */
|
|
if (pos->x >= drawdest.w)
|
|
return EUI_FALSE;
|
|
if (pos->y >= drawdest.h)
|
|
return EUI_FALSE;
|
|
if (pos->x + size->x < 0)
|
|
return EUI_FALSE;
|
|
if (pos->y + size->y < 0)
|
|
return EUI_FALSE;
|
|
|
|
/* clip to top edge */
|
|
if (pos->y < 0)
|
|
{
|
|
size->y += pos->y;
|
|
pos->y = 0;
|
|
}
|
|
|
|
/* clip to bottom edge */
|
|
if (pos->y + size->y >= drawdest.h)
|
|
{
|
|
size->y = drawdest.h - pos->y;
|
|
}
|
|
|
|
/* clip to left edge */
|
|
if (pos->x < 0)
|
|
{
|
|
size->x += pos->x;
|
|
pos->x = 0;
|
|
}
|
|
|
|
/* clip to right edge */
|
|
if (pos->x + size->x >= drawdest.w)
|
|
{
|
|
size->x = drawdest.w - pos->x;
|
|
}
|
|
|
|
return EUI_TRUE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* frame handling
|
|
*
|
|
*/
|
|
|
|
/* go to subframe, transformed from current frame */
|
|
void eui_push_frame(eui_vec2_t pos, eui_vec2_t size)
|
|
{
|
|
eui_transform_box(&pos, size);
|
|
|
|
frame_index++;
|
|
|
|
frames[frame_index].pos = pos;
|
|
frames[frame_index].size = size;
|
|
frames[frame_index].align.x = EUI_ALIGN_START;
|
|
frames[frame_index].align.y = EUI_ALIGN_START;
|
|
}
|
|
|
|
/* return to parent frame */
|
|
void eui_pop_frame(void)
|
|
{
|
|
if (frame_index)
|
|
frame_index--;
|
|
}
|
|
|
|
/* reset all frame transforms */
|
|
void eui_reset_frame(void)
|
|
{
|
|
frame_index = 0;
|
|
}
|
|
|
|
/* set current frame alignment */
|
|
void eui_set_align(int xalign, int yalign)
|
|
{
|
|
frames[frame_index].align.x = xalign;
|
|
frames[frame_index].align.y = yalign;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* event handling
|
|
*
|
|
*/
|
|
|
|
/* push event to the queue */
|
|
void eui_push_event(eui_event_t event)
|
|
{
|
|
if (num_events == MAX_EVENTS - 1)
|
|
{
|
|
printf("EUI WARNING: Event queue exhausted!\n");
|
|
return;
|
|
}
|
|
|
|
events[++num_events] = event;
|
|
}
|
|
|
|
/* pop event from the top of the queue */
|
|
int eui_pop_event(eui_event_t *out)
|
|
{
|
|
if (!num_events)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
*out = events[num_events];
|
|
memset(&events[num_events], 0, sizeof(eui_event_t));
|
|
num_events--;
|
|
|
|
return num_events + 1;
|
|
}
|
|
|
|
/* clear event queue */
|
|
void eui_clear_events(void)
|
|
{
|
|
num_events = 0;
|
|
memset(events, 0, sizeof(events));
|
|
mouse.x = 0;
|
|
mouse.y = 0;
|
|
button = 0;
|
|
memset(keys, 0, sizeof(keys));
|
|
memset(key_buffer, 0, sizeof(key_buffer));
|
|
}
|
|
|
|
/* push key to the queue */
|
|
void eui_push_key(int scancode)
|
|
{
|
|
key_buffer[key_buffer_widx] = scancode;
|
|
KEY_BUFFER_ADVANCE(key_buffer_widx);
|
|
if (key_buffer_widx == key_buffer_ridx)
|
|
KEY_BUFFER_ADVANCE(key_buffer_ridx);
|
|
}
|
|
|
|
/* pop key from the top of the queue */
|
|
int eui_pop_key(void)
|
|
{
|
|
int res = -1;
|
|
|
|
if (key_buffer_ridx == key_buffer_widx)
|
|
return res;
|
|
|
|
res = key_buffer[key_buffer_ridx];
|
|
KEY_BUFFER_ADVANCE(key_buffer_ridx);
|
|
|
|
return res;
|
|
}
|
|
|
|
/* reset keyboard queue */
|
|
static void eui_reset_key(void)
|
|
{
|
|
key_buffer_ridx = key_buffer_widx = 0;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* begin/end
|
|
*
|
|
*/
|
|
|
|
/* begin eui with given pixelmap destination */
|
|
int eui_begin(eui_pixelmap_t dest)
|
|
{
|
|
eui_event_t event;
|
|
eui_vec2_t pos, size;
|
|
|
|
/* sanity check */
|
|
if (!dest.w || !dest.h || !dest.pitch || !dest.pixels)
|
|
return EUI_FALSE;
|
|
if (dest.w >= MAX_WIDTH || dest.h >= MAX_HEIGHT)
|
|
return EUI_FALSE;
|
|
|
|
/* set draw destination */
|
|
drawdest = dest;
|
|
|
|
/* reset keyboard queue */
|
|
eui_reset_key();
|
|
|
|
/* process event queue */
|
|
while (eui_pop_event(&event))
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case EUI_EVENT_KEY_DOWN:
|
|
keys[event.key.scancode] = EUI_TRUE;
|
|
eui_push_key(event.key.scancode);
|
|
break;
|
|
|
|
case EUI_EVENT_KEY_UP:
|
|
keys[event.key.scancode] = EUI_FALSE;
|
|
break;
|
|
|
|
case EUI_EVENT_MOUSE:
|
|
mouse.x = event.mouse.x;
|
|
mouse.y = event.mouse.y;
|
|
break;
|
|
|
|
case EUI_EVENT_BUTTON_DOWN:
|
|
button |= event.button.button;
|
|
break;
|
|
|
|
case EUI_EVENT_BUTTON_UP:
|
|
button &= ~event.button.button;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* reset frame state */
|
|
eui_reset_frame();
|
|
|
|
/* push screen frame */
|
|
pos.x = 0;
|
|
pos.y = 0;
|
|
size.x = drawdest.w;
|
|
size.y = drawdest.h;
|
|
eui_push_frame(pos, size);
|
|
|
|
return EUI_TRUE;
|
|
}
|
|
|
|
/* end eui */
|
|
void eui_end(void)
|
|
{
|
|
|
|
}
|
|
|
|
/*
|
|
*
|
|
* configuration
|
|
*
|
|
*/
|
|
|
|
/* get modifiable config struct */
|
|
eui_config_t *eui_get_config(void)
|
|
{
|
|
return &config;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* utilities
|
|
*
|
|
*/
|
|
|
|
/* get dimensions of text string, with newlines */
|
|
eui_vec2_t eui_get_text_size(char *s)
|
|
{
|
|
int c;
|
|
eui_vec2_t pos;
|
|
eui_vec2_t size;
|
|
int lastx = 0;
|
|
|
|
pos.x = 0;
|
|
pos.y = 0;
|
|
|
|
/* find newlines in string */
|
|
while ((c = *s++))
|
|
{
|
|
if (c == '\n')
|
|
{
|
|
pos.y += 1;
|
|
lastx = pos.x;
|
|
pos.x = 0;
|
|
}
|
|
else
|
|
{
|
|
pos.x += 1;
|
|
}
|
|
}
|
|
|
|
size.x = lastx > pos.x ? lastx * 8 : pos.x * 8;
|
|
size.y = (pos.y + 1) * 8;
|
|
|
|
return size;
|
|
}
|
|
|
|
/* returns EUI_TRUE if the mouse cursor is hovering over the given area */
|
|
int eui_is_hovered(eui_vec2_t pos, eui_vec2_t size)
|
|
{
|
|
eui_transform_box(&pos, size);
|
|
|
|
if (mouse.x < pos.x || mouse.x > pos.x + size.x)
|
|
return EUI_FALSE;
|
|
if (mouse.y < pos.y || mouse.y > pos.y + size.y)
|
|
return EUI_FALSE;
|
|
return EUI_TRUE;
|
|
}
|
|
|
|
/* clear screen with color */
|
|
void eui_clear(eui_color_t color)
|
|
{
|
|
memset(drawdest.pixels, color, drawdest.h * drawdest.pitch);
|
|
}
|
|
|
|
/* draw built-in cursor */
|
|
void eui_cursor(eui_color_t color)
|
|
{
|
|
eui_vec2_t pos;
|
|
pos.x = mouse.x - 3;
|
|
pos.y = mouse.y - 3;
|
|
eui_font8x8(pos, font8x8_basic['x'], color);
|
|
}
|
|
|
|
/* get cursor position */
|
|
eui_vec2_t eui_get_cursor_pos(void)
|
|
{
|
|
return mouse;
|
|
}
|
|
|
|
/* get button state */
|
|
int eui_get_button(void)
|
|
{
|
|
return button;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* drawing primitives
|
|
*
|
|
*/
|
|
|
|
/* draw filled box at pos, clipped but not transformed */
|
|
static void eui_filled_box_absolute(eui_vec2_t pos, eui_vec2_t size, eui_color_t color)
|
|
{
|
|
int y;
|
|
|
|
if (!eui_clip_box(&pos, &size))
|
|
return;
|
|
|
|
for (y = pos.y; y < pos.y + size.y; y++)
|
|
{
|
|
memset(&PIXEL(drawdest, pos.x, y), color, size.x * sizeof(eui_color_t));
|
|
}
|
|
}
|
|
|
|
/* draw filled box at pos, transformed */
|
|
void eui_filled_box(eui_vec2_t pos, eui_vec2_t size, eui_color_t color)
|
|
{
|
|
eui_transform_box(&pos, size);
|
|
|
|
eui_filled_box_absolute(pos, size, color);
|
|
}
|
|
|
|
/* draw hollow box at pos, transformed */
|
|
void eui_border_box(eui_vec2_t pos, eui_vec2_t size, int width, eui_color_t color)
|
|
{
|
|
eui_vec2_t lpos, lsize;
|
|
eui_transform_box(&pos, size);
|
|
|
|
/* top line */
|
|
lpos.x = pos.x;
|
|
lpos.y = pos.y;
|
|
lsize.x = size.x;
|
|
lsize.y = width;
|
|
eui_filled_box_absolute(lpos, lsize, color);
|
|
|
|
/* bottom line */
|
|
lpos.x = pos.x;
|
|
lpos.y = pos.y + size.y - width;
|
|
lsize.x = size.x;
|
|
lsize.y = width;
|
|
eui_filled_box_absolute(lpos, lsize, color);
|
|
|
|
/* left line */
|
|
lpos.x = pos.x;
|
|
lpos.y = pos.y + width;
|
|
lsize.x = width;
|
|
lsize.y = size.y - width * 2;
|
|
eui_filled_box_absolute(lpos, lsize, color);
|
|
|
|
/* right line */
|
|
lpos.x = pos.x + size.x - width;
|
|
lpos.y = pos.y + width;
|
|
lsize.x = width;
|
|
lsize.y = size.y - width * 2;
|
|
eui_filled_box_absolute(lpos, lsize, color);
|
|
}
|
|
|
|
/* draw text at pos, transformed */
|
|
void eui_text(eui_vec2_t pos, eui_color_t color, char *s)
|
|
{
|
|
eui_vec2_t size;
|
|
int c;
|
|
int start_x;
|
|
size_t len = 0;
|
|
char *ptr;
|
|
|
|
/* get text size */
|
|
size = eui_get_text_size(s);
|
|
|
|
/* transform to size */
|
|
eui_transform_box(&pos, size);
|
|
|
|
/* draw string */
|
|
start_x = pos.x;
|
|
while ((c = *s++))
|
|
{
|
|
if (c == '\n')
|
|
{
|
|
switch (frames[frame_index].align.x)
|
|
{
|
|
case EUI_ALIGN_START:
|
|
/* set next string position */
|
|
pos.x = start_x;
|
|
pos.y += 8;
|
|
break;
|
|
|
|
case EUI_ALIGN_MIDDLE:
|
|
/* get length of next string chunk */
|
|
ptr = s;
|
|
while (*ptr && *ptr != '\n') ptr++;
|
|
len = ptr - s;
|
|
/* set next string position */
|
|
pos.x = start_x + (size.x / 2) - ((len * 8) / 2);
|
|
pos.y += 8;
|
|
break;
|
|
|
|
case EUI_ALIGN_END:
|
|
/* get length of next string chunk */
|
|
ptr = s;
|
|
while (*ptr && *ptr != '\n') ptr++;
|
|
len = ptr - s;
|
|
/* set next string position */
|
|
pos.x = start_x + size.x - (len * 8);
|
|
pos.y += 8;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eui_font8x8(pos, font8x8_basic[c], color);
|
|
pos.x += 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* draw formatted text at pos, transformed */
|
|
void eui_textf(eui_vec2_t pos, eui_color_t color, char *s, ...)
|
|
{
|
|
static char text[1024];
|
|
va_list args;
|
|
|
|
va_start(args, s);
|
|
vsprintf(text, s, args);
|
|
va_end(args);
|
|
|
|
eui_text(pos, color, text);
|
|
}
|
|
|
|
/* scan triangle edge and add to edge table */
|
|
static void eui_triangle_scan_edge(eui_vec2_t p0, eui_vec2_t p1, int edge_table[MAX_HEIGHT][2])
|
|
{
|
|
int sx, sy, dx1, dy1, dx2, dy2, x, y, m, n, k, cnt;
|
|
|
|
sx = p1.x - p0.x;
|
|
sy = p1.y - p0.y;
|
|
|
|
if (sx > 0)
|
|
dx1 = 1;
|
|
else if (sx < 0)
|
|
dx1 = -1;
|
|
else
|
|
dx1 = 0;
|
|
|
|
if (sy > 0)
|
|
dy1 = 1;
|
|
else if (sy < 0)
|
|
dy1 = -1;
|
|
else
|
|
dy1 = 0;
|
|
|
|
m = abs(sx);
|
|
n = abs(sy);
|
|
dx2 = dx1;
|
|
dy2 = 0;
|
|
|
|
if (m < n)
|
|
{
|
|
m = abs(sy);
|
|
n = abs(sx);
|
|
dx2 = 0;
|
|
dy2 = dy1;
|
|
}
|
|
|
|
x = p0.x;
|
|
y = p0.y;
|
|
cnt = m + 1;
|
|
k = n / 2;
|
|
|
|
while (cnt--)
|
|
{
|
|
if (y >= 0 && y < drawdest.h)
|
|
{
|
|
if (x < edge_table[y][0])
|
|
edge_table[y][0] = x;
|
|
if (x > edge_table[y][1])
|
|
edge_table[y][1] = x;
|
|
}
|
|
|
|
k += n;
|
|
|
|
if (k < m)
|
|
{
|
|
x += dx2;
|
|
y += dy2;
|
|
}
|
|
else
|
|
{
|
|
k -= m;
|
|
x += dx1;
|
|
y += dy1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* draw filled triangle with provided points, transformed */
|
|
void eui_filled_triangle(eui_vec2_t p0, eui_vec2_t p1, eui_vec2_t p2, eui_color_t color)
|
|
{
|
|
int x, y, len;
|
|
static int edge_table[MAX_HEIGHT][2];
|
|
|
|
/* init edge table */
|
|
for (y = 0; y < drawdest.h; y++)
|
|
{
|
|
edge_table[y][0] = INT_MAX;
|
|
edge_table[y][1] = INT_MIN;
|
|
}
|
|
|
|
/* transform points */
|
|
eui_transform_point(&p0);
|
|
eui_transform_point(&p1);
|
|
eui_transform_point(&p2);
|
|
|
|
/* scan triangle edges */
|
|
eui_triangle_scan_edge(p0, p1, edge_table);
|
|
eui_triangle_scan_edge(p1, p2, edge_table);
|
|
eui_triangle_scan_edge(p2, p0, edge_table);
|
|
|
|
for (y = 0; y < drawdest.h; y++)
|
|
{
|
|
if (edge_table[y][1] >= edge_table[y][0])
|
|
{
|
|
x = edge_table[y][0];
|
|
len = 1 + edge_table[y][1] - edge_table[y][0];
|
|
|
|
while (len--)
|
|
{
|
|
PIXEL(drawdest, x++, y) = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* draw line from p0 to p1, transformed */
|
|
void eui_line(eui_vec2_t p0, eui_vec2_t p1, eui_color_t color)
|
|
{
|
|
int i, dx, dy, sdx, sdy, dxabs, dyabs, x, y, px, py;
|
|
|
|
/* transform points */
|
|
eui_transform_point(&p0);
|
|
eui_transform_point(&p1);
|
|
|
|
/* the horizontal distance of the line */
|
|
dx = p1.x - p0.x;
|
|
|
|
/* the vertical distance of the line */
|
|
dy = p1.y - p0.y;
|
|
|
|
dxabs = abs(dx);
|
|
dyabs = abs(dy);
|
|
|
|
sdx = sgn(dx);
|
|
sdy = sgn(dy);
|
|
|
|
x = dyabs >> 1;
|
|
y = dxabs >> 1;
|
|
|
|
px = p0.x;
|
|
py = p0.y;
|
|
|
|
if (px >= 0 && px < drawdest.w && py >= 0 && py < drawdest.h)
|
|
PIXEL(drawdest, px, py) = color;
|
|
|
|
if (dxabs >= dyabs)
|
|
{
|
|
/* the line is more horizontal than vertical */
|
|
for (i = 0; i < dxabs; i++)
|
|
{
|
|
y += dyabs;
|
|
|
|
if (y >= dxabs)
|
|
{
|
|
y -= dxabs;
|
|
py += sdy;
|
|
}
|
|
|
|
px += sdx;
|
|
|
|
if (px < 0 || px >= drawdest.w)
|
|
continue;
|
|
if (py < 0 || py >= drawdest.h)
|
|
continue;
|
|
|
|
PIXEL(drawdest, px, py) = color;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* the line is more vertical than horizontal */
|
|
for (i = 0; i < dyabs; i++)
|
|
{
|
|
x += dxabs;
|
|
|
|
if ( x >= dyabs)
|
|
{
|
|
x -= dyabs;
|
|
px += sdx;
|
|
}
|
|
|
|
py += sdy;
|
|
|
|
if (px < 0 || px >= drawdest.w)
|
|
continue;
|
|
if (py < 0 || py >= drawdest.h)
|
|
continue;
|
|
|
|
PIXEL(drawdest, px, py) = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* draw pixelmap, transformed */
|
|
void eui_pixelmap(eui_vec2_t pos, eui_pixelmap_t pixelmap)
|
|
{
|
|
int y, yy;
|
|
eui_vec2_t size;
|
|
eui_vec2_t ofs;
|
|
|
|
/* transform */
|
|
size.x = pixelmap.w;
|
|
size.y = pixelmap.h;
|
|
eui_transform_box(&pos, size);
|
|
|
|
/* save current pos */
|
|
ofs = pos;
|
|
|
|
/* clip */
|
|
if (!eui_clip_box(&pos, &size))
|
|
return;
|
|
|
|
/* calculate pixelmap ofs */
|
|
if (ofs.x < pos.x)
|
|
ofs.x = pos.x - ofs.x;
|
|
else
|
|
ofs.x = 0;
|
|
if (ofs.y < pos.y)
|
|
ofs.y = pos.y - ofs.y;
|
|
else
|
|
ofs.y = 0;
|
|
|
|
/* draw */
|
|
for (y = ofs.y; y < size.y; y++)
|
|
{
|
|
yy = pos.y + y - ofs.y;
|
|
memcpy(&PIXEL(drawdest, pos.x, yy), &PIXEL(pixelmap, ofs.x, y), size.x);
|
|
}
|
|
}
|
|
|
|
/* draw xbm graphic, transformed */
|
|
void eui_xbm(eui_vec2_t pos, eui_color_t color, int w, int h, unsigned char *bitmap)
|
|
{
|
|
int x, y, xx, yy;
|
|
int pitch;
|
|
eui_vec2_t size;
|
|
|
|
/* transform */
|
|
size.x = w;
|
|
size.y = h;
|
|
eui_transform_box(&pos, size);
|
|
|
|
/* draw graphic */
|
|
pitch = w / 8 + ((w % 8) ? 1 : 0);
|
|
for (y = 0; y < h; y++)
|
|
{
|
|
for (x = 0; x < w; x++)
|
|
{
|
|
if (bitmap[y * pitch + (x / 8)] & 1 << (x % 8))
|
|
{
|
|
xx = pos.x + x;
|
|
yy = pos.y + y;
|
|
|
|
if (xx < 0 || xx >= drawdest.w || yy < 0 || yy >= drawdest.h)
|
|
continue;
|
|
|
|
PIXEL(drawdest, xx, yy) = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* widgets
|
|
*
|
|
*/
|
|
|
|
/* fires callback function if pressed and returns EUI_TRUE if hovered */
|
|
int eui_button(eui_vec2_t pos, eui_vec2_t size, char *text, eui_callback callback, void *user)
|
|
{
|
|
static int clicked;
|
|
int hovered;
|
|
eui_vec2_t zeropos;
|
|
|
|
hovered = eui_is_hovered(pos, size);
|
|
|
|
zeropos.x = 0;
|
|
zeropos.y = 0;
|
|
|
|
if (hovered)
|
|
{
|
|
eui_filled_box(pos, size, config.button.bg_color_hover);
|
|
eui_border_box(pos, size, config.button.border_width, config.button.border_color_hover);
|
|
eui_push_frame(pos, size);
|
|
eui_set_align(EUI_ALIGN_MIDDLE, EUI_ALIGN_MIDDLE);
|
|
eui_text(zeropos, config.button.text_color_hover, text);
|
|
eui_pop_frame();
|
|
}
|
|
else
|
|
{
|
|
eui_filled_box(pos, size, config.button.bg_color);
|
|
eui_border_box(pos, size, config.button.border_width, config.button.border_color);
|
|
eui_push_frame(pos, size);
|
|
eui_set_align(EUI_ALIGN_MIDDLE, EUI_ALIGN_MIDDLE);
|
|
eui_text(zeropos, config.button.text_color, text);
|
|
eui_pop_frame();
|
|
}
|
|
|
|
if (hovered && button && !clicked)
|
|
{
|
|
callback(user);
|
|
clicked = EUI_TRUE;
|
|
}
|
|
|
|
if (!button)
|
|
clicked = EUI_FALSE;
|
|
|
|
return hovered;
|
|
}
|
|
|
|
/* fires callback function if pressed and returns EUI_TRUE if hovered (xbm graphic) */
|
|
int eui_button_xbm(eui_vec2_t pos, int w, int h, unsigned char *bits, eui_callback callback, void *user)
|
|
{
|
|
static int clicked;
|
|
int hovered;
|
|
eui_vec2_t size;
|
|
|
|
size.x = w;
|
|
size.y = h;
|
|
|
|
hovered = eui_is_hovered(pos, size);
|
|
|
|
if (hovered)
|
|
eui_xbm(pos, config.button.border_color_hover, w, h, bits);
|
|
else
|
|
eui_xbm(pos, config.button.border_color, w, h, bits);
|
|
|
|
if (hovered && button && !clicked)
|
|
{
|
|
callback(user);
|
|
clicked = EUI_TRUE;
|
|
}
|
|
|
|
if (!button)
|
|
clicked = EUI_FALSE;
|
|
|
|
return hovered;
|
|
}
|
|
|
|
/* on/off checkbox */
|
|
void eui_checkbox(eui_vec2_t pos, char *label, eui_color_t color, int *value)
|
|
{
|
|
static int clicked;
|
|
int hovered;
|
|
eui_vec2_t size;
|
|
|
|
if (!value)
|
|
return;
|
|
|
|
/* box border */
|
|
size.x = 11;
|
|
size.y = 11;
|
|
eui_xbm(pos, color, checkbox_border_w, checkbox_border_h, checkbox_border_bits);
|
|
|
|
/* toggle on click */
|
|
hovered = eui_is_hovered(pos, size);
|
|
if (hovered && button && !clicked)
|
|
{
|
|
*value = !*value;
|
|
clicked = EUI_TRUE;
|
|
}
|
|
if (!button)
|
|
clicked = EUI_FALSE;
|
|
|
|
/* draw x */
|
|
if (*value)
|
|
eui_xbm(pos, color, checkbox_x_w, checkbox_x_h, checkbox_x_bits);
|
|
|
|
/* label */
|
|
pos.x += 13;
|
|
pos.y += 2;
|
|
eui_text(pos, color, label);
|
|
}
|