pull in more code from PicoDrive
authornotaz <notasas@gmail.com>
Thu, 9 Dec 2010 16:56:03 +0000 (18:56 +0200)
committernotaz <notasas@gmail.com>
Tue, 14 Dec 2010 13:25:04 +0000 (15:25 +0200)
17 files changed:
frontend/common/fonts.c [new file with mode: 0644]
frontend/common/fonts.h [new file with mode: 0644]
frontend/common/input.c [new file with mode: 0644]
frontend/common/input.h [new file with mode: 0644]
frontend/common/lprintf.h [new file with mode: 0644]
frontend/common/menu.c [new file with mode: 0644]
frontend/common/menu.h [new file with mode: 0644]
frontend/common/plat.h [new file with mode: 0644]
frontend/common/posix.h [new file with mode: 0644]
frontend/common/readpng.c [new file with mode: 0644]
frontend/common/readpng.h [new file with mode: 0644]
frontend/linux/fbdev.c
frontend/linux/in_evdev.c [new file with mode: 0644]
frontend/linux/in_evdev.h [new file with mode: 0644]
frontend/linux/oshide.c [new file with mode: 0644]
frontend/linux/oshide.h [new file with mode: 0644]
frontend/linux/plat.c [new file with mode: 0644]

diff --git a/frontend/common/fonts.c b/frontend/common/fonts.c
new file mode 100644 (file)
index 0000000..ea65481
--- /dev/null
@@ -0,0 +1,218 @@
+unsigned char fontdata8x8[64*16] =
+{
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C,
+       0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
+       0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00,
+       0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00,
+       0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00,
+       0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40,
+       0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00,
+       0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00,
+       0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00,
+       0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00,
+       0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00,
+       0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00,
+       0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00,
+       0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00,
+       0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00,
+       0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00,
+       0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00,
+       0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00,
+       0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00,
+       0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00,
+       0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30,
+       0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00,
+       0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00,
+       0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00,
+       0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00,
+       0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00,
+       0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00,
+       0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,
+       0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00,
+       0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00,
+       0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
+       0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00,
+       0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00,
+       0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,
+       0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00,
+       0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00,
+       0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,
+       0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,
+       0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
+       0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00,
+       0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00,
+       0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00,
+       0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C,
+       0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00,
+       0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00,
+       0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00,
+       0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00,
+       0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06,
+       0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00,
+       0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00,
+       0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00,
+       0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C,
+       0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00,
+       0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00,
+       0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00,
+};
+
+
+/* The font is generated from Xorg clR5x8.bdf */
+/*
+COMMENT  Copyright 1989 Dale Schumacher, dal@syntel.mn.org
+COMMENT                 399 Beacon Ave.
+COMMENT                 St. Paul, MN  55104-3527
+COMMENT
+COMMENT  Permission to use, copy, modify, and distribute this software and
+COMMENT  its documentation for any purpose and without fee is hereby
+COMMENT  granted, provided that the above copyright notice appear in all
+COMMENT  copies and that both that copyright notice and this permission
+COMMENT  notice appear in supporting documentation, and that the name of
+COMMENT  Dale Schumacher not be used in advertising or publicity pertaining to
+COMMENT  distribution of the software without specific, written prior
+COMMENT  permission.  Dale Schumacher makes no representations about the
+COMMENT  suitability of this software for any purpose.  It is provided "as
+COMMENT  is" without express or implied warranty.
+COMMENT
+*/
+unsigned char fontdata6x8[256][8] = {
+{ 0x7c>>2, 0x44>>2, 0x7c>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0xff>>2, 0x7c>>2, },
+{ 0x00>>2, 0x00>>2, 0x10>>2, 0x28>>2, 0x44>>2, 0x7c>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x78>>2, 0x44>>2, 0x78>>2, 0x44>>2, 0x78>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x38>>2, 0x44>>2, 0x40>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x78>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x78>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x7c>>2, 0x40>>2, 0x78>>2, 0x40>>2, 0x7c>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x7c>>2, 0x40>>2, 0x78>>2, 0x40>>2, 0x40>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x3c>>2, 0x40>>2, 0x4c>>2, 0x44>>2, 0x3c>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x44>>2, 0x7c>>2, 0x44>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x38>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x38>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x1c>>2, 0x04>>2, 0x04>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x48>>2, 0x50>>2, 0x60>>2, 0x50>>2, 0x48>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x7c>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x6c>>2, 0x54>>2, 0x54>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x64>>2, 0x54>>2, 0x4c>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x38>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x78>>2, 0x44>>2, 0x78>>2, 0x40>>2, 0x40>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x38>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x0c>>2, },
+{ 0x00>>2, 0x00>>2, 0x78>>2, 0x44>>2, 0x78>>2, 0x50>>2, 0x4c>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x3c>>2, 0x40>>2, 0x38>>2, 0x04>>2, 0x78>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x7c>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x44>>2, 0x28>>2, 0x28>>2, 0x10>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x54>>2, 0x54>>2, 0x6c>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x28>>2, 0x10>>2, 0x28>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x28>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x7c>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x7c>>2, 0x00>>2, },
+{ 0xe0>>2, 0x80>>2, 0xe0>>2, 0x8c>>2, 0xf0>>2, 0x10>>2, 0x10>>2, 0x0c>>2, },
+{ 0x00>>2, 0x10>>2, 0x38>>2, 0x7c>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x00>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x7c>>2, 0x38>>2, 0x10>>2, 0x00>>2, },
+{ 0x00>>2, 0x10>>2, 0x18>>2, 0xfc>>2, 0x18>>2, 0x10>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x20>>2, 0x60>>2, 0xfc>>2, 0x60>>2, 0x20>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, 0x10>>2, 0x00>>2, },
+{ 0x28>>2, 0x28>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x28>>2, 0x7c>>2, 0x28>>2, 0x7c>>2, 0x28>>2, 0x00>>2, 0x00>>2, },
+{ 0x10>>2, 0x3c>>2, 0x50>>2, 0x38>>2, 0x14>>2, 0x78>>2, 0x10>>2, 0x00>>2, },
+{ 0x60>>2, 0x64>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x4c>>2, 0x0c>>2, 0x00>>2, },
+{ 0x38>>2, 0x40>>2, 0x40>>2, 0x20>>2, 0x54>>2, 0x48>>2, 0x34>>2, 0x00>>2, },
+{ 0x10>>2, 0x20>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x04>>2, 0x08>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x08>>2, 0x04>>2, 0x00>>2, },
+{ 0x40>>2, 0x20>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x00>>2, },
+{ 0x00>>2, 0x10>>2, 0x54>>2, 0x38>>2, 0x54>>2, 0x10>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x10>>2, 0x10>>2, 0x7c>>2, 0x10>>2, 0x10>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x10>>2, 0x10>>2, 0x20>>2, },
+{ 0x00>>2, 0x00>>2, 0x00>>2, 0x7c>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x04>>2, 0x04>>2, 0x08>>2, 0x08>>2, 0x10>>2, 0x10>>2, 0x20>>2, 0x20>>2, },
+{ 0x38>>2, 0x44>>2, 0x4c>>2, 0x54>>2, 0x64>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x10>>2, 0x30>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x04>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x7c>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x04>>2, 0x18>>2, 0x04>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x18>>2, 0x18>>2, 0x28>>2, 0x28>>2, 0x7c>>2, 0x08>>2, 0x1c>>2, 0x00>>2, },
+{ 0x7c>>2, 0x40>>2, 0x78>>2, 0x04>>2, 0x04>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x18>>2, 0x20>>2, 0x40>>2, 0x78>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x7c>>2, 0x44>>2, 0x04>>2, 0x08>>2, 0x08>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x44>>2, 0x3c>>2, 0x04>>2, 0x08>>2, 0x30>>2, 0x00>>2, },
+{ 0x00>>2, 0x10>>2, 0x10>>2, 0x00>>2, 0x00>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x00>>2, 0x10>>2, 0x10>>2, 0x00>>2, 0x00>>2, 0x10>>2, 0x10>>2, 0x20>>2, },
+{ 0x00>>2, 0x0c>>2, 0x30>>2, 0xc0>>2, 0x30>>2, 0x0c>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x7c>>2, 0x00>>2, 0x7c>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0xc0>>2, 0x30>>2, 0x0c>>2, 0x30>>2, 0xc0>>2, 0x00>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x04>>2, 0x08>>2, 0x10>>2, 0x00>>2, 0x10>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x5c>>2, 0x5c>>2, 0x58>>2, 0x40>>2, 0x38>>2, 0x00>>2, },
+{ 0x10>>2, 0x28>>2, 0x44>>2, 0x44>>2, 0x7c>>2, 0x44>>2, 0x44>>2, 0x00>>2, },
+{ 0x78>>2, 0x44>>2, 0x44>>2, 0x78>>2, 0x44>>2, 0x44>>2, 0x78>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x70>>2, 0x48>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x48>>2, 0x70>>2, 0x00>>2, },
+{ 0x7c>>2, 0x40>>2, 0x40>>2, 0x78>>2, 0x40>>2, 0x40>>2, 0x7c>>2, 0x00>>2, },
+{ 0x7c>>2, 0x40>>2, 0x40>>2, 0x78>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x40>>2, 0x4c>>2, 0x44>>2, 0x44>>2, 0x3c>>2, 0x00>>2, },
+{ 0x44>>2, 0x44>>2, 0x44>>2, 0x7c>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x00>>2, },
+{ 0x7c>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x7c>>2, 0x00>>2, },
+{ 0x1c>>2, 0x04>>2, 0x04>>2, 0x04>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x44>>2, 0x48>>2, 0x50>>2, 0x60>>2, 0x50>>2, 0x48>>2, 0x44>>2, 0x00>>2, },
+{ 0x40>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x7c>>2, 0x00>>2, },
+{ 0x44>>2, 0x6c>>2, 0x54>>2, 0x54>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x00>>2, },
+{ 0x44>>2, 0x64>>2, 0x64>>2, 0x54>>2, 0x4c>>2, 0x4c>>2, 0x44>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x78>>2, 0x44>>2, 0x44>>2, 0x78>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x0c>>2, },
+{ 0x78>>2, 0x44>>2, 0x44>>2, 0x78>>2, 0x50>>2, 0x48>>2, 0x44>>2, 0x00>>2, },
+{ 0x38>>2, 0x44>>2, 0x40>>2, 0x38>>2, 0x04>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x7c>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x44>>2, 0x44>>2, 0x44>>2, 0x28>>2, 0x28>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x44>>2, 0x44>>2, 0x44>>2, 0x54>>2, 0x54>>2, 0x6c>>2, 0x44>>2, 0x00>>2, },
+{ 0x44>>2, 0x44>>2, 0x28>>2, 0x10>>2, 0x28>>2, 0x44>>2, 0x44>>2, 0x00>>2, },
+{ 0x44>>2, 0x44>>2, 0x28>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x7c>>2, 0x04>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x40>>2, 0x7c>>2, 0x00>>2, },
+{ 0x1c>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x1c>>2, 0x00>>2, },
+{ 0x20>>2, 0x20>>2, 0x10>>2, 0x10>>2, 0x08>>2, 0x08>>2, 0x04>>2, 0x04>>2, },
+{ 0x70>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x70>>2, 0x00>>2, },
+{ 0x10>>2, 0x28>>2, 0x44>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0xfc>>2, 0x00>>2, },
+{ 0x10>>2, 0x08>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x3c>>2, 0x44>>2, 0x44>>2, 0x4c>>2, 0x34>>2, 0x00>>2, },
+{ 0x40>>2, 0x40>>2, 0x78>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x78>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x3c>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x3c>>2, 0x00>>2, },
+{ 0x04>>2, 0x04>>2, 0x3c>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x3c>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x38>>2, 0x44>>2, 0x7c>>2, 0x40>>2, 0x38>>2, 0x00>>2, },
+{ 0x1c>>2, 0x20>>2, 0x78>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x20>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x3c>>2, 0x44>>2, 0x44>>2, 0x3c>>2, 0x04>>2, 0x38>>2, },
+{ 0x40>>2, 0x40>>2, 0x78>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x00>>2, },
+{ 0x10>>2, 0x00>>2, 0x30>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x38>>2, 0x00>>2, },
+{ 0x08>>2, 0x00>>2, 0x38>>2, 0x08>>2, 0x08>>2, 0x08>>2, 0x08>>2, 0x70>>2, },
+{ 0x40>>2, 0x40>>2, 0x48>>2, 0x50>>2, 0x60>>2, 0x50>>2, 0x48>>2, 0x00>>2, },
+{ 0x30>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x38>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x68>>2, 0x54>>2, 0x54>>2, 0x54>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x58>>2, 0x64>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x38>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x38>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x78>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x78>>2, 0x40>>2, },
+{ 0x00>>2, 0x00>>2, 0x3c>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x3c>>2, 0x04>>2, },
+{ 0x00>>2, 0x00>>2, 0x58>>2, 0x60>>2, 0x40>>2, 0x40>>2, 0x40>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x3c>>2, 0x40>>2, 0x38>>2, 0x04>>2, 0x78>>2, 0x00>>2, },
+{ 0x10>>2, 0x10>>2, 0x7c>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x0c>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x4c>>2, 0x34>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x6c>>2, 0x28>>2, 0x28>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x54>>2, 0x54>>2, 0x54>>2, 0x28>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x28>>2, 0x10>>2, 0x28>>2, 0x44>>2, 0x00>>2, },
+{ 0x00>>2, 0x00>>2, 0x44>>2, 0x44>>2, 0x44>>2, 0x3c>>2, 0x04>>2, 0x38>>2, },
+{ 0x00>>2, 0x00>>2, 0x7c>>2, 0x08>>2, 0x10>>2, 0x20>>2, 0x7c>>2, 0x00>>2, },
+{ 0x04>>2, 0x08>>2, 0x08>>2, 0x10>>2, 0x08>>2, 0x08>>2, 0x04>>2, 0x00>>2, },
+{ 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x10>>2, 0x00>>2, },
+{ 0x40>>2, 0x20>>2, 0x20>>2, 0x10>>2, 0x20>>2, 0x20>>2, 0x40>>2, 0x00>>2, },
+{ 0x20>>2, 0x54>>2, 0x08>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, 0x00>>2, },
+{ 0x00>>2, 0x10>>2, 0x10>>2, 0x28>>2, 0x28>>2, 0x44>>2, 0x7c>>2, 0x00>>2, },
+};
+
diff --git a/frontend/common/fonts.h b/frontend/common/fonts.h
new file mode 100644 (file)
index 0000000..dbbdd4c
--- /dev/null
@@ -0,0 +1,4 @@
+
+extern unsigned char fontdata8x8[64*16];
+extern unsigned char fontdata6x8[256-32][8];
+
diff --git a/frontend/common/input.c b/frontend/common/input.c
new file mode 100644 (file)
index 0000000..7bbb39c
--- /dev/null
@@ -0,0 +1,965 @@
+/*
+ * (C) Gražvydas "notaz" Ignotas, 2008-2010
+ *
+ * This work is licensed under the terms of any of these licenses
+ * (at your option):
+ *  - GNU GPL, version 2 or later.
+ *  - GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "input.h"
+#include "plat.h"
+#include "lprintf.h"
+
+#ifdef IN_EVDEV
+#include "../linux/in_evdev.h"
+#endif
+#ifdef IN_GP2X
+#include "../gp2x/in_gp2x.h"
+#endif
+#ifdef IN_VK
+#include "../win32/in_vk.h"
+#endif
+
+typedef struct
+{
+       int drv_id;
+       int drv_fd_hnd;
+       void *drv_data;
+       char *name;
+       int key_count;
+       int *binds;     /* total = key_count * bindtypes * 2 */
+       const char * const *key_names;
+       unsigned int probed:1;
+       unsigned int does_combos:1;
+} in_dev_t;
+
+static in_drv_t in_drivers[IN_DRVID_COUNT];
+static in_dev_t in_devices[IN_MAX_DEVS];
+static int in_dev_count = 0;
+static int in_have_async_devs = 0;
+static int menu_key_state = 0;
+static int menu_last_used_dev = 0;
+
+#define DRV(id) in_drivers[(unsigned)(id) < IN_DRVID_COUNT ? (id) : 0]
+
+
+static int *in_alloc_binds(int drv_id, int key_count)
+{
+       int *binds;
+
+       binds = calloc(key_count * IN_BINDTYPE_COUNT * 2, sizeof(binds[0]));
+       if (binds == NULL)
+               return NULL;
+
+       DRV(drv_id).get_def_binds(binds + key_count * IN_BINDTYPE_COUNT);
+       memcpy(binds, binds + key_count * IN_BINDTYPE_COUNT,
+               sizeof(binds[0]) * key_count * IN_BINDTYPE_COUNT);
+
+       return binds;
+}
+
+static void in_free(in_dev_t *dev)
+{
+       if (dev->probed)
+               DRV(dev->drv_id).free(dev->drv_data);
+       dev->probed = 0;
+       dev->drv_data = NULL;
+       free(dev->name);
+       dev->name = NULL;
+       free(dev->binds);
+       dev->binds = NULL;
+}
+
+/* to be called by drivers
+ * async devices must set drv_fd_hnd to -1 */
+void in_register(const char *nname, int drv_id, int drv_fd_hnd, void *drv_data,
+               int key_count, const char * const *key_names, int combos)
+{
+       int i, ret, dupe_count = 0, *binds;
+       char name[256], *name_end, *tmp;
+
+       strncpy(name, nname, sizeof(name));
+       name[sizeof(name)-12] = 0;
+       name_end = name + strlen(name);
+
+       for (i = 0; i < in_dev_count; i++)
+       {
+               if (in_devices[i].name == NULL)
+                       continue;
+               if (strcmp(in_devices[i].name, name) == 0)
+               {
+                       if (in_devices[i].probed) {
+                               dupe_count++;
+                               sprintf(name_end, " [%d]", dupe_count);
+                               continue;
+                       }
+                       goto update;
+               }
+       }
+
+       if (i >= IN_MAX_DEVS)
+       {
+               /* try to find unused device */
+               for (i = 0; i < IN_MAX_DEVS; i++)
+                       if (!in_devices[i].probed) break;
+               if (i >= IN_MAX_DEVS) {
+                       lprintf("input: too many devices, can't add %s\n", name);
+                       return;
+               }
+               in_free(&in_devices[i]);
+       }
+
+       tmp = strdup(name);
+       if (tmp == NULL)
+               return;
+
+       binds = in_alloc_binds(drv_id, key_count);
+       if (binds == NULL) {
+               free(tmp);
+               return;
+       }
+
+       in_devices[i].name = tmp;
+       in_devices[i].binds = binds;
+       in_devices[i].key_count = key_count;
+       if (i + 1 > in_dev_count)
+               in_dev_count = i + 1;
+
+       lprintf("input: new device #%d \"%s\"\n", i, name);
+update:
+       in_devices[i].probed = 1;
+       in_devices[i].does_combos = combos;
+       in_devices[i].drv_id = drv_id;
+       in_devices[i].drv_fd_hnd = drv_fd_hnd;
+       in_devices[i].key_names = key_names;
+       in_devices[i].drv_data = drv_data;
+
+       if (in_devices[i].binds != NULL) {
+               ret = DRV(drv_id).clean_binds(drv_data, in_devices[i].binds,
+                       in_devices[i].binds + key_count * IN_BINDTYPE_COUNT);
+               if (ret == 0) {
+                       /* no useable binds */
+                       free(in_devices[i].binds);
+                       in_devices[i].binds = NULL;
+               }
+       }
+}
+
+/* key combo handling, to be called by drivers that support it.
+ * Only care about IN_BINDTYPE_EMU */
+void in_combos_find(const int *binds, int last_key, int *combo_keys, int *combo_acts)
+{
+       int act, u;
+
+       *combo_keys = *combo_acts = 0;
+       for (act = 0; act < sizeof(binds[0]) * 8; act++)
+       {
+               int keyc = 0;
+               for (u = 0; u <= last_key; u++)
+                       if (binds[IN_BIND_OFFS(u, IN_BINDTYPE_EMU)] & (1 << act))
+                               keyc++;
+
+               if (keyc > 1)
+               {
+                       // loop again and mark those keys and actions as combo
+                       for (u = 0; u <= last_key; u++)
+                       {
+                               if (binds[IN_BIND_OFFS(u, IN_BINDTYPE_EMU)] & (1 << act)) {
+                                       *combo_keys |= 1 << u;
+                                       *combo_acts |= 1 << act;
+                               }
+                       }
+               }
+       }
+}
+
+int in_combos_do(int keys, const int *binds, int last_key, int combo_keys, int combo_acts)
+{
+       int i, ret = 0;
+
+       for (i = 0; i <= last_key; i++)
+       {
+               int acts, acts_c, u;
+
+               if (!(keys & (1 << i)))
+                       continue;
+
+               acts = binds[IN_BIND_OFFS(i, IN_BINDTYPE_EMU)];
+               if (!acts)
+                       continue;
+
+               if (!(combo_keys & (1 << i))) {
+                       ret |= acts;
+                       continue;
+               }
+
+               acts_c = acts & combo_acts;
+               u = last_key;
+               if (acts_c) {
+                       // let's try to find the other one
+                       for (u = i + 1; u <= last_key; u++)
+                               if ( (keys & (1 << u)) && (binds[IN_BIND_OFFS(u, IN_BINDTYPE_EMU)] & acts_c) ) {
+                                       ret |= acts_c & binds[IN_BIND_OFFS(u, IN_BINDTYPE_EMU)];
+                                       keys &= ~((1 << i) | (1 << u));
+                                       break;
+                               }
+               }
+               // add non-combo actions if combo ones were not found
+               if (u >= last_key)
+                       ret |= acts & ~combo_acts;
+       }
+
+       return ret;
+}
+
+void in_probe(void)
+{
+       int i;
+
+       in_have_async_devs = 0;
+       for (i = 0; i < in_dev_count; i++)
+               in_devices[i].probed = 0;
+
+       for (i = 1; i < IN_DRVID_COUNT; i++)
+               in_drivers[i].probe();
+
+       /* get rid of devs without binds and probes */
+       for (i = 0; i < in_dev_count; i++) {
+               if (!in_devices[i].probed && in_devices[i].binds == NULL) {
+                       in_dev_count--;
+                       if (i < in_dev_count) {
+                               free(in_devices[i].name);
+                               memmove(&in_devices[i], &in_devices[i+1],
+                                       (in_dev_count - i) * sizeof(in_devices[0]));
+                       }
+
+                       continue;
+               }
+
+               if (in_devices[i].probed && in_devices[i].drv_fd_hnd == -1)
+                       in_have_async_devs = 1;
+       }
+
+       if (in_have_async_devs)
+               lprintf("input: async-only devices detected..\n");
+
+       in_debug_dump();
+}
+
+/* async update */
+int in_update(int *result)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < in_dev_count; i++) {
+               in_dev_t *dev = &in_devices[i];
+               if (dev->probed && dev->binds != NULL) {
+                       // FIXME: this is stupid, make it indirect
+                       switch (dev->drv_id) {
+#ifdef IN_EVDEV
+                       case IN_DRVID_EVDEV:
+                               ret |= in_evdev_update(dev->drv_data, dev->binds, result);
+                               break;
+#endif
+#ifdef IN_GP2X
+                       case IN_DRVID_GP2X:
+                               ret |= in_gp2x_update(dev->drv_data, dev->binds, result);
+                               break;
+#endif
+#ifdef IN_VK
+                       case IN_DRVID_VK:
+                               ret |= in_vk_update(dev->drv_data, dev->binds, result);
+                               break;
+#endif
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static int in_update_kc_async(int *dev_id_out, int *is_down_out, int timeout_ms)
+{
+       int i, is_down, result;
+       unsigned int ticks;
+
+       ticks = plat_get_ticks_ms();
+
+       while (1)
+       {
+               for (i = 0; i < in_dev_count; i++) {
+                       in_dev_t *d = &in_devices[i];
+                       if (!d->probed)
+                               continue;
+
+                       result = DRV(d->drv_id).update_keycode(d->drv_data, &is_down);
+                       if (result == -1)
+                               continue;
+
+                       if (dev_id_out)
+                               *dev_id_out = i;
+                       if (is_down_out)
+                               *is_down_out = is_down;
+                       return result;
+               }
+
+               if (timeout_ms >= 0 && (int)(plat_get_ticks_ms() - ticks) > timeout_ms)
+                       break;
+
+               plat_sleep_ms(10);
+       }
+
+       return -1;
+}
+
+/* 
+ * wait for a press, always return some keycode or -1 on timeout or error
+ */
+int in_update_keycode(int *dev_id_out, int *is_down_out, int timeout_ms)
+{
+       int result = -1, dev_id = 0, is_down, result_menu;
+       int fds_hnds[IN_MAX_DEVS];
+       int i, ret, count = 0;
+       in_drv_t *drv = NULL;
+       unsigned int ticks;
+
+       if (in_have_async_devs) {
+               result = in_update_kc_async(&dev_id, &is_down, timeout_ms);
+               if (result == -1)
+                       return -1;
+               drv = &DRV(in_devices[dev_id].drv_id);
+               goto finish;
+       }
+
+       ticks = plat_get_ticks_ms();
+
+       for (i = 0; i < in_dev_count; i++) {
+               if (in_devices[i].probed)
+                       fds_hnds[count++] = in_devices[i].drv_fd_hnd;
+       }
+
+       if (count == 0) {
+               /* don't deadlock, fail */
+               lprintf("input: failed to find devices to read\n");
+               exit(1);
+       }
+
+       while (1)
+       {
+               ret = plat_wait_event(fds_hnds, count, timeout_ms);
+               if (ret < 0)
+                       break;
+
+               for (i = 0; i < in_dev_count; i++) {
+                       if (in_devices[i].drv_fd_hnd == ret) {
+                               dev_id = i;
+                               break;
+                       }
+               }
+
+               drv = &DRV(in_devices[dev_id].drv_id);
+               result = drv->update_keycode(in_devices[dev_id].drv_data, &is_down);
+               if (result >= 0)
+                       break;
+
+               if (timeout_ms >= 0) {
+                       unsigned int ticks2 = plat_get_ticks_ms();
+                       timeout_ms -= ticks2 - ticks;
+                       ticks = ticks2;
+                       if (timeout_ms <= 0)
+                               break;
+               }
+       }
+
+       if (result < 0)
+               return -1;
+finish:
+       /* keep track of menu key state, to allow mixing
+        * in_update_keycode() and in_menu_wait_any() calls */
+       result_menu = drv->menu_translate(in_devices[dev_id].drv_data, result);
+       if (result_menu != 0) {
+               if (is_down)
+                       menu_key_state |=  result_menu;
+               else
+                       menu_key_state &= ~result_menu;
+       }
+
+       if (dev_id_out != NULL)
+               *dev_id_out = dev_id;
+       if (is_down_out != NULL)
+               *is_down_out = is_down;
+       return result;
+}
+
+/* same as above, only return bitfield of PBTN_*  */
+int in_menu_wait_any(int timeout_ms)
+{
+       int keys_old = menu_key_state;
+
+       while (1)
+       {
+               int code, is_down = 0, dev_id = 0;
+
+               code = in_update_keycode(&dev_id, &is_down, timeout_ms);
+               if (code < 0)
+                       break;
+
+               if (keys_old != menu_key_state) {
+                       menu_last_used_dev = dev_id;
+                       break;
+               }
+       }
+
+       return menu_key_state;
+}
+
+/* wait for menu input, do autorepeat */
+int in_menu_wait(int interesting, int autorep_delay_ms)
+{
+       static int inp_prev = 0;
+       static int repeats = 0;
+       int ret, release = 0, wait = 450;
+
+       if (repeats)
+               wait = autorep_delay_ms;
+
+       ret = in_menu_wait_any(wait);
+       if (ret == inp_prev)
+               repeats++;
+
+       while (!(ret & interesting)) {
+               ret = in_menu_wait_any(-1);
+               release = 1;
+       }
+
+       if (release || ret != inp_prev)
+               repeats = 0;
+
+       inp_prev = ret;
+
+       /* we don't need diagonals in menus */
+       if ((ret & PBTN_UP)   && (ret & PBTN_LEFT))  ret &= ~PBTN_LEFT;
+       if ((ret & PBTN_UP)   && (ret & PBTN_RIGHT)) ret &= ~PBTN_RIGHT;
+       if ((ret & PBTN_DOWN) && (ret & PBTN_LEFT))  ret &= ~PBTN_LEFT;
+       if ((ret & PBTN_DOWN) && (ret & PBTN_RIGHT)) ret &= ~PBTN_RIGHT;
+
+       return ret;
+}
+
+const int *in_get_dev_binds(int dev_id)
+{
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS)
+               return NULL;
+
+       return in_devices[dev_id].binds;
+}
+
+const int *in_get_dev_def_binds(int dev_id)
+{
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS)
+               return NULL;
+
+       return in_devices[dev_id].binds + in_devices[dev_id].key_count * IN_BINDTYPE_COUNT;
+}
+
+int in_get_config(int dev_id, int what, void *val)
+{
+       int *ival = val;
+       in_dev_t *dev;
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS || val == NULL)
+               return -1;
+
+       dev = &in_devices[dev_id];
+       switch (what) {
+       case IN_CFG_BIND_COUNT:
+               *ival = dev->key_count;
+               break;
+       case IN_CFG_DOES_COMBOS:
+               *ival = dev->does_combos;
+               break;
+       case IN_CFG_BLOCKING:
+       case IN_CFG_KEY_NAMES:
+               return -1; /* not implemented */
+       default:
+               return DRV(dev->drv_id).get_config(dev->drv_data, what, ival);
+       }
+
+       return 0;
+}
+
+static int in_set_blocking(int is_blocking)
+{
+       int i, ret;
+
+       /* have_async_devs means we will have to do all reads async anyway.. */
+       if (!in_have_async_devs) {
+               for (i = 0; i < in_dev_count; i++) {
+                       if (in_devices[i].probed)
+                               DRV(in_devices[i].drv_id).set_config(in_devices[i].drv_data,
+                                                                    IN_CFG_BLOCKING, is_blocking);
+               }
+       }
+
+       menu_key_state = 0;
+
+       /* flush events */
+       do {
+               ret = in_update_keycode(NULL, NULL, 0);
+       } while (ret >= 0);
+
+       return 0;
+}
+
+int in_set_config(int dev_id, int what, const void *val, int size)
+{
+       const int *ival = val;
+       in_dev_t *dev;
+
+       if (what == IN_CFG_BLOCKING)
+               return in_set_blocking(*ival);
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS)
+               return -1;
+
+       dev = &in_devices[dev_id];
+       if (what == IN_CFG_KEY_NAMES) {
+               const char * const *names = val;
+               int count = size / sizeof(names[0]);
+
+               if (count < dev->key_count) {
+                       lprintf("input: set_key_names: not enough keys\n");
+                       return -1;
+               }
+
+               dev->key_names = names;
+               return 0;
+       }
+
+       return DRV(dev->drv_id).set_config(dev->drv_data, what, *ival);
+}
+
+const char *in_get_dev_name(int dev_id, int must_be_active, int skip_pfix)
+{
+       const char *name, *tmp;
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS)
+               return NULL;
+
+       if (must_be_active && !in_devices[dev_id].probed)
+               return NULL;
+
+       name = in_devices[dev_id].name;
+       if (name == NULL || !skip_pfix)
+               return name;
+
+       /* skip prefix */
+       tmp = strchr(name, ':');
+       if (tmp != NULL)
+               name = tmp + 1;
+
+       return name;
+}
+
+int in_name_to_id(const char *dev_name)
+{
+       int i;
+
+       for (i = 0; i < in_dev_count; i++)
+               if (strcmp(dev_name, in_devices[i].name) == 0)
+                       break;
+
+       if (i >= in_dev_count) {
+               lprintf("input: in_name_to_id: no such device: %s\n", dev_name);
+               return -1;
+       }
+
+       return i;
+}
+
+/* never returns NULL */
+const char *in_get_key_name(int dev_id, int keycode)
+{
+       const char *name = NULL;
+       static char xname[16];
+       in_dev_t *dev;
+
+       if (dev_id < 0)         /* want last used dev? */
+               dev_id = menu_last_used_dev;
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS)
+               return "Unkn0";
+
+       dev = &in_devices[dev_id];
+       if (keycode < 0)        /* want name for menu key? */
+               keycode = DRV(dev->drv_id).menu_translate(dev->drv_data, keycode);
+
+       if (dev->key_names != NULL && 0 <= keycode && keycode < dev->key_count)
+               name = dev->key_names[keycode];
+       if (name != NULL)
+               return name;
+
+       name = DRV(dev->drv_id).get_key_name(keycode);
+       if (name != NULL)
+               return name;
+
+       /* assume scancode */
+       if ((keycode >= '0' && keycode <= '9') || (keycode >= 'a' && keycode <= 'z')
+                       || (keycode >= 'A' && keycode <= 'Z'))
+               sprintf(xname, "%c", keycode);
+       else
+               sprintf(xname, "\\x%02X", keycode);
+       return xname;
+}
+
+int in_get_key_code(int dev_id, const char *key_name)
+{
+       in_dev_t *dev;
+       int i;
+
+       if (dev_id < 0)         /* want last used dev? */
+               dev_id = menu_last_used_dev;
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS)
+               return -1;
+
+       dev = &in_devices[dev_id];
+       if (dev->key_names == NULL)
+               return -1;
+
+       for (i = 0; i < dev->key_count; i++)
+               if (dev->key_names[i] && strcasecmp(dev->key_names[i], key_name) == 0)
+                       return i;
+
+       return -1;
+}
+
+int in_bind_key(int dev_id, int keycode, int mask, int bind_type, int force_unbind)
+{
+       int ret, count;
+       in_dev_t *dev;
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS || bind_type >= IN_BINDTYPE_COUNT)
+               return -1;
+
+       dev = &in_devices[dev_id];
+       count = dev->key_count;
+
+       if (dev->binds == NULL) {
+               if (force_unbind)
+                       return 0;
+               dev->binds = in_alloc_binds(dev->drv_id, count);
+               if (dev->binds == NULL)
+                       return -1;
+       }
+
+       if (keycode < 0 || keycode >= count)
+               return -1;
+       
+       if (force_unbind)
+               dev->binds[IN_BIND_OFFS(keycode, bind_type)] &= ~mask;
+       else
+               dev->binds[IN_BIND_OFFS(keycode, bind_type)] ^=  mask;
+       
+       ret = DRV(dev->drv_id).clean_binds(dev->drv_data, dev->binds,
+                               dev->binds + count * IN_BINDTYPE_COUNT);
+       if (ret == 0) {
+               free(dev->binds);
+               dev->binds = NULL;
+       }
+
+       return 0;
+}
+
+void in_unbind_all(int dev_id, int act_mask, int bind_type)
+{
+       int i, count;
+       in_dev_t *dev;
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS || bind_type >= IN_BINDTYPE_COUNT)
+               return;
+
+       dev = &in_devices[dev_id];
+       count = dev->key_count;
+
+       if (dev->binds == NULL)
+               return;
+
+       for (i = 0; i < count; i++)
+               dev->binds[IN_BIND_OFFS(i, bind_type)] &= ~act_mask;
+}
+
+/* returns device id, or -1 on error */
+int in_config_parse_dev(const char *name)
+{
+       int drv_id = -1, i;
+
+       for (i = 0; i < IN_DRVID_COUNT; i++) {
+               int len = strlen(in_drivers[i].prefix);
+               if (strncmp(name, in_drivers[i].prefix, len) == 0) {
+                       drv_id = i;
+                       break;
+               }
+       }
+
+       if (drv_id < 0) {
+               lprintf("input: missing driver for %s\n", name);
+               return -1;
+       }
+
+       for (i = 0; i < in_dev_count; i++)
+       {
+               if (in_devices[i].name == NULL)
+                       continue;
+               if (strcmp(in_devices[i].name, name) == 0)
+                       return i;
+       }
+
+       if (i >= IN_MAX_DEVS)
+       {
+               /* try to find unused device */
+               for (i = 0; i < IN_MAX_DEVS; i++)
+                       if (in_devices[i].name == NULL) break;
+               if (i >= IN_MAX_DEVS) {
+                       lprintf("input: too many devices, can't add %s\n", name);
+                       return -1;
+               }
+       }
+
+       memset(&in_devices[i], 0, sizeof(in_devices[i]));
+
+       in_devices[i].name = strdup(name);
+       if (in_devices[i].name == NULL)
+               return -1;
+
+       in_devices[i].key_count = DRV(drv_id).get_bind_count();
+       in_devices[i].drv_id = drv_id;
+
+       if (i + 1 > in_dev_count)
+               in_dev_count = i + 1;
+
+       return i;
+}
+
+/*
+ * To reduce size of game specific configs, default binds are not saved.
+ * So we mark default binds in in_config_start(), override them in in_config_bind_key(),
+ * and restore whatever default binds are left in in_config_end().
+ */
+void in_config_start(void)
+{
+       int i;
+
+       /* mark all default binds, so they get overwritten by func below */
+       for (i = 0; i < IN_MAX_DEVS; i++) {
+               int n, count, *binds, *def_binds;
+
+               binds = in_devices[i].binds;
+               if (binds == NULL)
+                       continue;
+
+               count = in_devices[i].key_count;
+               def_binds = binds + count * IN_BINDTYPE_COUNT;
+
+               for (n = 0; n < count * IN_BINDTYPE_COUNT; n++)
+                       if (binds[n] == def_binds[n])
+                               binds[n] = -1;
+       }
+}
+
+int in_config_bind_key(int dev_id, const char *key, int acts, int bind_type)
+{
+       in_dev_t *dev;
+       int i, offs, kc;
+
+       if (dev_id < 0 || dev_id >= IN_MAX_DEVS || bind_type >= IN_BINDTYPE_COUNT)
+               return -1;
+       dev = &in_devices[dev_id];
+
+       /* maybe a raw code? */
+       if (key[0] == '\\' && key[1] == 'x') {
+               char *p = NULL;
+               kc = (int)strtoul(key + 2, &p, 16);
+               if (p == NULL || *p != 0)
+                       kc = -1;
+       }
+       else {
+               /* device specific key name */
+               if (dev->binds == NULL) {
+                       dev->binds = in_alloc_binds(dev->drv_id, dev->key_count);
+                       if (dev->binds == NULL)
+                               return -1;
+                       in_config_start();
+               }
+
+               kc = -1;
+               if (dev->key_names != NULL) {
+                       for (i = 0; i < dev->key_count; i++) {
+                               const char *k = dev->key_names[i];
+                               if (k != NULL && strcasecmp(k, key) == 0) {
+                                       kc = i;
+                                       break;
+                               }
+                       }
+               }
+
+               if (kc < 0)
+                       kc = DRV(dev->drv_id).get_key_code(key);
+               if (kc < 0 && strlen(key) == 1) {
+                       /* assume scancode */
+                       kc = key[0];
+               }
+       }
+
+       if (kc < 0 || kc >= dev->key_count) {
+               lprintf("input: bad key: %s\n", key);
+               return -1;
+       }
+
+       if (bind_type == IN_BINDTYPE_NONE) {
+               for (i = 0; i < IN_BINDTYPE_COUNT; i++)
+                       dev->binds[IN_BIND_OFFS(kc, i)] = 0;
+               return 0;
+       }
+
+       offs = IN_BIND_OFFS(kc, bind_type);
+       if (dev->binds[offs] == -1)
+               dev->binds[offs] = 0;
+       dev->binds[offs] |= acts;
+       return 0;
+}
+
+void in_config_end(void)
+{
+       int i;
+
+       for (i = 0; i < IN_MAX_DEVS; i++) {
+               int n, t, ret, count, *binds, *def_binds;
+               in_dev_t *dev = &in_devices[i];
+
+               if (dev->binds == NULL)
+                       continue;
+
+               count = dev->key_count;
+               binds = dev->binds;
+               def_binds = binds + count * IN_BINDTYPE_COUNT;
+
+               for (n = 0; n < count; n++) {
+                       int is_default = 1;
+                       for (t = 0; t < IN_BINDTYPE_COUNT; t++)
+                               if (binds[IN_BIND_OFFS(n, t)] == -1)
+                                       binds[IN_BIND_OFFS(n, t)] = 0;
+                               else
+                                       is_default = 0;
+
+                       if (is_default)
+                               for (t = 0; t < IN_BINDTYPE_COUNT; t++)
+                                       binds[IN_BIND_OFFS(n, t)] = def_binds[IN_BIND_OFFS(n, t)];
+               }
+
+               if (dev->drv_data == NULL)
+                       continue;
+
+               ret = DRV(dev->drv_id).clean_binds(dev->drv_data, binds, def_binds);
+               if (ret == 0) {
+                       /* no useable binds */
+                       free(dev->binds);
+                       dev->binds = NULL;
+               }
+       }
+}
+
+void in_debug_dump(void)
+{
+       int i;
+
+       lprintf("# drv probed binds name\n");
+       for (i = 0; i < IN_MAX_DEVS; i++) {
+               in_dev_t *d = &in_devices[i];
+               if (!d->probed && d->name == NULL && d->binds == NULL)
+                       continue;
+               lprintf("%d %3d %6c %5c %s\n", i, d->drv_id, d->probed ? 'y' : 'n',
+                       d->binds ? 'y' : 'n', d->name);
+       }
+}
+
+/* handlers for unknown/not_preset drivers */
+
+static void in_def_probe(void) {}
+static void in_def_free(void *drv_data) {}
+static int  in_def_get_bind_count(void) { return 0; }
+static void in_def_get_def_binds(int *binds) {}
+static int  in_def_clean_binds(void *drv_data, int *b, int *db) { return 0; }
+static int  in_def_get_config(void *drv_data, int what, int *val) { return -1; }
+static int  in_def_set_config(void *drv_data, int what, int val) { return -1; }
+static int  in_def_update_keycode(void *drv_data, int *is_down) { return 0; }
+static int  in_def_menu_translate(void *drv_data, int keycode) { return keycode; }
+static int  in_def_get_key_code(const char *key_name) { return -1; }
+static const char *in_def_get_key_name(int keycode) { return NULL; }
+
+void in_init(void)
+{
+       int i;
+
+       memset(in_drivers, 0, sizeof(in_drivers));
+       memset(in_devices, 0, sizeof(in_devices));
+       in_dev_count = 0;
+
+       for (i = 0; i < IN_DRVID_COUNT; i++) {
+               in_drivers[i].prefix = "none:";
+               in_drivers[i].probe = in_def_probe;
+               in_drivers[i].free = in_def_free;
+               in_drivers[i].get_bind_count = in_def_get_bind_count;
+               in_drivers[i].get_def_binds = in_def_get_def_binds;
+               in_drivers[i].clean_binds = in_def_clean_binds;
+               in_drivers[i].get_config = in_def_get_config;
+               in_drivers[i].set_config = in_def_set_config;
+               in_drivers[i].update_keycode = in_def_update_keycode;
+               in_drivers[i].menu_translate = in_def_menu_translate;
+               in_drivers[i].get_key_code = in_def_get_key_code;
+               in_drivers[i].get_key_name = in_def_get_key_name;
+       }
+
+#ifdef IN_GP2X
+       in_gp2x_init(&in_drivers[IN_DRVID_GP2X]);
+#endif
+#ifdef IN_EVDEV
+       in_evdev_init(&in_drivers[IN_DRVID_EVDEV]);
+#endif
+#ifdef IN_VK
+       in_vk_init(&in_drivers[IN_DRVID_VK]);
+#endif
+}
+
+#if 0
+int main(void)
+{
+       int ret;
+
+       in_init();
+       in_probe();
+
+       in_set_blocking(1);
+
+#if 1
+       while (1) {
+               int dev = 0, down;
+               ret = in_update_keycode(&dev, &down);
+               lprintf("#%i: %i %i (%s)\n", dev, down, ret, in_get_key_name(dev, ret));
+       }
+#else
+       while (1) {
+               ret = in_menu_wait_any();
+               lprintf("%08x\n", ret);
+       }
+#endif
+
+       return 0;
+}
+#endif
diff --git a/frontend/common/input.h b/frontend/common/input.h
new file mode 100644 (file)
index 0000000..41ff876
--- /dev/null
@@ -0,0 +1,128 @@
+#define IN_MAX_DEVS 10
+
+/* unified menu keys */
+#define PBTN_UP    (1 <<  0)
+#define PBTN_DOWN  (1 <<  1)
+#define PBTN_LEFT  (1 <<  2)
+#define PBTN_RIGHT (1 <<  3)
+
+#define PBTN_MOK   (1 <<  4)
+#define PBTN_MBACK (1 <<  5)
+#define PBTN_MA2   (1 <<  6)   /* menu action 2 */
+#define PBTN_MA3   (1 <<  7)
+
+#define PBTN_L     (1 <<  8)
+#define PBTN_R     (1 <<  9)
+
+#define PBTN_MENU  (1 << 10)
+
+/* ui events */
+#define PEVB_VOL_DOWN   30
+#define PEVB_VOL_UP     29
+#define PEVB_STATE_LOAD 28
+#define PEVB_STATE_SAVE 27
+#define PEVB_SWITCH_RND 26
+#define PEVB_SSLOT_PREV 25
+#define PEVB_SSLOT_NEXT 24
+#define PEVB_MENU       23
+#define PEVB_FF         22
+#define PEVB_PICO_PNEXT 21
+#define PEVB_PICO_PPREV 20
+#define PEVB_PICO_SWINP 19
+
+#define PEV_VOL_DOWN    (1 << PEVB_VOL_DOWN)
+#define PEV_VOL_UP      (1 << PEVB_VOL_UP)
+#define PEV_STATE_LOAD  (1 << PEVB_STATE_LOAD)
+#define PEV_STATE_SAVE  (1 << PEVB_STATE_SAVE)
+#define PEV_SWITCH_RND  (1 << PEVB_SWITCH_RND)
+#define PEV_SSLOT_PREV  (1 << PEVB_SSLOT_PREV)
+#define PEV_SSLOT_NEXT  (1 << PEVB_SSLOT_NEXT)
+#define PEV_MENU        (1 << PEVB_MENU)
+#define PEV_FF          (1 << PEVB_FF)
+#define PEV_PICO_PNEXT  (1 << PEVB_PICO_PNEXT)
+#define PEV_PICO_PPREV  (1 << PEVB_PICO_PPREV)
+#define PEV_PICO_SWINP  (1 << PEVB_PICO_SWINP)
+
+#define PEV_MASK 0x7ff80000
+
+
+enum {
+       IN_DRVID_UNKNOWN = 0,
+       IN_DRVID_GP2X,
+       IN_DRVID_EVDEV,
+       IN_DRVID_VK,
+       IN_DRVID_COUNT,
+};
+
+enum {
+       IN_CFG_BIND_COUNT = 0,
+       IN_CFG_DOES_COMBOS,
+       IN_CFG_BLOCKING,
+       IN_CFG_KEY_NAMES,
+       IN_CFG_ABS_DEAD_ZONE, /* dead zone for analog-digital mapping */
+};
+
+enum {
+       IN_BINDTYPE_NONE = -1,
+       IN_BINDTYPE_EMU = 0,
+       IN_BINDTYPE_PLAYER12,
+       IN_BINDTYPE_COUNT
+};
+
+#define IN_BIND_OFFS(key, btype) \
+       ((key) * IN_BINDTYPE_COUNT + (btype))
+
+typedef struct {
+       const char *prefix;
+       void (*probe)(void);
+       void (*free)(void *drv_data);
+       int  (*get_bind_count)(void);
+       void (*get_def_binds)(int *binds);
+       int  (*clean_binds)(void *drv_data, int *binds, int *def_finds);
+       int  (*get_config)(void *drv_data, int what, int *val);
+       int  (*set_config)(void *drv_data, int what, int val);
+       int  (*update_keycode)(void *drv_data, int *is_down);
+       int  (*menu_translate)(void *drv_data, int keycode);
+       int  (*get_key_code)(const char *key_name);
+       const char * (*get_key_name)(int keycode);
+} in_drv_t;
+
+struct in_default_bind {
+       unsigned short code;
+       unsigned char btype;    /* IN_BINDTYPE_* */
+       unsigned char bit;
+};
+
+/* to be called by drivers */
+void in_register(const char *nname, int drv_id, int drv_fd_hnd, void *drv_data,
+               int key_count, const char * const *key_names, int combos);
+void in_combos_find(const int *binds, int last_key, int *combo_keys, int *combo_acts);
+int  in_combos_do(int keys, const int *binds, int last_key, int combo_keys, int combo_acts);
+
+void in_init(void);
+void in_probe(void);
+int  in_update(int *result);
+int  in_update_keycode(int *dev_id, int *is_down, int timeout_ms);
+int  in_menu_wait_any(int timeout_ms);
+int  in_menu_wait(int interesting, int autorep_delay_ms);
+void in_config_start(void);
+int  in_config_parse_dev(const char *dev_name);
+int  in_config_bind_key(int dev_id, const char *key, int binds, int bind_type);
+void in_config_end(void);
+int  in_get_config(int dev_id, int what, void *val);
+int  in_set_config(int dev_id, int what, const void *val, int size);
+int  in_get_key_code(int dev_id, const char *key_name);
+int  in_name_to_id(const char *dev_name);
+int  in_bind_key(int dev_id, int keycode, int mask, int bind_type, int force_unbind);
+void in_unbind_all(int dev_id, int act_mask, int bind_type);
+void in_debug_dump(void);
+
+const int  *in_get_dev_binds(int dev_id);
+const int  *in_get_dev_def_binds(int dev_id);
+const char *in_get_dev_name(int dev_id, int must_be_active, int skip_pfix);
+const char *in_get_key_name(int dev_id, int keycode);
+
+#define in_set_config_int(dev_id, what, v) { \
+       int val_ = v; \
+       in_set_config(dev_id, what, &val_, sizeof(val_)); \
+}
diff --git a/frontend/common/lprintf.h b/frontend/common/lprintf.h
new file mode 100644 (file)
index 0000000..48b8d57
--- /dev/null
@@ -0,0 +1,10 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void lprintf(const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/frontend/common/menu.c b/frontend/common/menu.c
new file mode 100644 (file)
index 0000000..1cb41c5
--- /dev/null
@@ -0,0 +1,1247 @@
+// (c) Copyright 2006-2010 notaz, All rights reserved.\r
+// Free for non-commercial use.\r
+\r
+// For commercial use, separate licencing terms must be obtained.\r
+\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+\r
+#include "menu.h"\r
+#include "fonts.h"\r
+#include "readpng.h"\r
+#include "lprintf.h"\r
+#include "input.h"\r
+#include "plat.h"\r
+#include "posix.h"\r
+\r
+static char static_buff[64];\r
+static int  menu_error_time = 0;\r
+char menu_error_msg[64] = { 0, };\r
+void *g_menuscreen_ptr;\r
+void *g_menubg_src_ptr;\r
+void *g_menubg_ptr;\r
+\r
+#if !MSCREEN_SIZE_FIXED\r
+int g_menuscreen_w;\r
+int g_menuscreen_h;\r
+#endif\r
+\r
+static unsigned char *menu_font_data = NULL;\r
+static int menu_text_color = 0xffff; // default to white\r
+static int menu_sel_color = -1; // disabled\r
+\r
+/* note: these might become non-constant in future */\r
+#if MENU_X2\r
+static const int me_mfont_w = 16, me_mfont_h = 20;\r
+static const int me_sfont_w = 12, me_sfont_h = 20;\r
+#else\r
+static const int me_mfont_w = 8, me_mfont_h = 10;\r
+static const int me_sfont_w = 6, me_sfont_h = 10;\r
+#endif\r
+\r
+// draws text to current bbp16 screen\r
+static void text_out16_(int x, int y, const char *text, int color)\r
+{\r
+       int i, lh, tr, tg, tb, len;\r
+       unsigned short *dest = (unsigned short *)g_menuscreen_ptr + x + y * g_menuscreen_w;\r
+       tr = (color & 0xf800) >> 8;\r
+       tg = (color & 0x07e0) >> 3;\r
+       tb = (color & 0x001f) << 3;\r
+\r
+       if (text == (void *)1)\r
+       {\r
+               // selector symbol\r
+               text = "";\r
+               len = 1;\r
+       }\r
+       else\r
+       {\r
+               const char *p;\r
+               for (p = text; *p != 0 && *p != '\n'; p++)\r
+                       ;\r
+               len = p - text;\r
+       }\r
+\r
+       lh = me_mfont_h;\r
+       if (y + lh > g_menuscreen_h)\r
+               lh = g_menuscreen_h - y;\r
+\r
+       for (i = 0; i < len; i++)\r
+       {\r
+               unsigned char  *src = menu_font_data + (unsigned int)text[i] * me_mfont_w * me_mfont_h / 2;\r
+               unsigned short *dst = dest;\r
+               int u, l;\r
+\r
+               for (l = 0; l < lh; l++, dst += g_menuscreen_w - me_mfont_w)\r
+               {\r
+                       for (u = me_mfont_w / 2; u > 0; u--, src++)\r
+                       {\r
+                               int c, r, g, b;\r
+                               c = *src >> 4;\r
+                               r = (*dst & 0xf800) >> 8;\r
+                               g = (*dst & 0x07e0) >> 3;\r
+                               b = (*dst & 0x001f) << 3;\r
+                               r = (c^0xf)*r/15 + c*tr/15;\r
+                               g = (c^0xf)*g/15 + c*tg/15;\r
+                               b = (c^0xf)*b/15 + c*tb/15;\r
+                               *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
+                               c = *src & 0xf;\r
+                               r = (*dst & 0xf800) >> 8;\r
+                               g = (*dst & 0x07e0) >> 3;\r
+                               b = (*dst & 0x001f) << 3;\r
+                               r = (c^0xf)*r/15 + c*tr/15;\r
+                               g = (c^0xf)*g/15 + c*tg/15;\r
+                               b = (c^0xf)*b/15 + c*tb/15;\r
+                               *dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);\r
+                       }\r
+               }\r
+               dest += me_mfont_w;\r
+       }\r
+}\r
+\r
+void text_out16(int x, int y, const char *texto, ...)\r
+{\r
+       va_list args;\r
+       char    buffer[256];\r
+       int     maxw = (g_menuscreen_w - x) / me_mfont_w;\r
+\r
+       if (maxw < 0)\r
+               return;\r
+\r
+       va_start(args, texto);\r
+       vsnprintf(buffer, sizeof(buffer), texto, args);\r
+       va_end(args);\r
+\r
+       if (maxw > sizeof(buffer) - 1)\r
+               maxw = sizeof(buffer) - 1;\r
+       buffer[maxw] = 0;\r
+\r
+       text_out16_(x,y,buffer,menu_text_color);\r
+}\r
+\r
+/* draws in 6x8 font, might multiply size by integer */\r
+static void smalltext_out16_(int x, int y, const char *texto, int color)\r
+{\r
+       unsigned char  *src;\r
+       unsigned short *dst;\r
+       int multiplier = me_sfont_w / 6;\r
+       int i;\r
+\r
+       for (i = 0;; i++, x += me_sfont_w)\r
+       {\r
+               unsigned char c = (unsigned char) texto[i];\r
+               int h = 8;\r
+\r
+               if (!c || c == '\n')\r
+                       break;\r
+\r
+               src = fontdata6x8[c];\r
+               dst = (unsigned short *)g_menuscreen_ptr + x + y * g_menuscreen_w;\r
+\r
+               while (h--)\r
+               {\r
+                       int m, w2, h2;\r
+                       for (h2 = multiplier; h2 > 0; h2--)\r
+                       {\r
+                               for (m = 0x20; m; m >>= 1) {\r
+                                       if (*src & m)\r
+                                               for (w2 = multiplier; w2 > 0; w2--)\r
+                                                       *dst++ = color;\r
+                                       else\r
+                                               dst += multiplier;\r
+                               }\r
+\r
+                               dst += g_menuscreen_w - me_sfont_w;\r
+                       }\r
+                       src++;\r
+               }\r
+       }\r
+}\r
+\r
+static void smalltext_out16(int x, int y, const char *texto, int color)\r
+{\r
+       char buffer[128];\r
+       int maxw = (g_menuscreen_w - x) / me_sfont_w;\r
+\r
+       if (maxw < 0)\r
+               return;\r
+\r
+       strncpy(buffer, texto, sizeof(buffer));\r
+       if (maxw > sizeof(buffer) - 1)\r
+               maxw = sizeof(buffer) - 1;\r
+       buffer[maxw] = 0;\r
+\r
+       smalltext_out16_(x, y, buffer, color);\r
+}\r
+\r
+static void menu_draw_selection(int x, int y, int w)\r
+{\r
+       int i, h;\r
+       unsigned short *dst, *dest;\r
+\r
+       text_out16_(x, y, (void *)1, (menu_sel_color < 0) ? menu_text_color : menu_sel_color);\r
+\r
+       if (menu_sel_color < 0) return; // no selection hilight\r
+\r
+       if (y > 0) y--;\r
+       dest = (unsigned short *)g_menuscreen_ptr + x + y * g_menuscreen_w + me_mfont_w * 2 - 2;\r
+       for (h = me_mfont_h + 1; h > 0; h--)\r
+       {\r
+               dst = dest;\r
+               for (i = w - (me_mfont_w * 2 - 2); i > 0; i--)\r
+                       *dst++ = menu_sel_color;\r
+               dest += g_menuscreen_w;\r
+       }\r
+}\r
+\r
+static int parse_hex_color(char *buff)\r
+{\r
+       char *endp = buff;\r
+       int t = (int) strtoul(buff, &endp, 16);\r
+       if (endp != buff)\r
+#ifdef PSP\r
+               return ((t<<8)&0xf800) | ((t>>5)&0x07e0) | ((t>>19)&0x1f);\r
+#else\r
+               return ((t>>8)&0xf800) | ((t>>5)&0x07e0) | ((t>>3)&0x1f);\r
+#endif\r
+       return -1;\r
+}\r
+\r
+void menu_init(void)\r
+{\r
+       int i, c, l;\r
+       unsigned char *fd, *fds;\r
+       char buff[256];\r
+       FILE *f;\r
+\r
+       if (menu_font_data != NULL)\r
+               free(menu_font_data);\r
+\r
+       menu_font_data = calloc((MENU_X2 ? 256 * 320 : 128 * 160) / 2, 1);\r
+       if (menu_font_data == NULL)\r
+               return;\r
+\r
+       // generate default 8x10 font from fontdata8x8\r
+       for (c = 0, fd = menu_font_data; c < 256; c++)\r
+       {\r
+               for (l = 0; l < 8; l++)\r
+               {\r
+                       unsigned char fd8x8 = fontdata8x8[c*8+l];\r
+                       if (fd8x8&0x80) *fd  = 0xf0;\r
+                       if (fd8x8&0x40) *fd |= 0x0f; fd++;\r
+                       if (fd8x8&0x20) *fd  = 0xf0;\r
+                       if (fd8x8&0x10) *fd |= 0x0f; fd++;\r
+                       if (fd8x8&0x08) *fd  = 0xf0;\r
+                       if (fd8x8&0x04) *fd |= 0x0f; fd++;\r
+                       if (fd8x8&0x02) *fd  = 0xf0;\r
+                       if (fd8x8&0x01) *fd |= 0x0f; fd++;\r
+               }\r
+               fd += 8*2/2; // 2 empty lines\r
+       }\r
+\r
+       if (MENU_X2) {\r
+               // expand default font\r
+               fds = menu_font_data + 128 * 160 / 2 - 4;\r
+               fd  = menu_font_data + 256 * 320 / 2 - 1;\r
+               for (c = 255; c >= 0; c--)\r
+               {\r
+                       for (l = 9; l >= 0; l--, fds -= 4)\r
+                       {\r
+                               for (i = 3; i >= 0; i--) {\r
+                                       int px = fds[i] & 0x0f;\r
+                                       *fd-- = px | (px << 4);\r
+                                       px = (fds[i] >> 4) & 0x0f;\r
+                                       *fd-- = px | (px << 4);\r
+                               }\r
+                               for (i = 3; i >= 0; i--) {\r
+                                       int px = fds[i] & 0x0f;\r
+                                       *fd-- = px | (px << 4);\r
+                                       px = (fds[i] >> 4) & 0x0f;\r
+                                       *fd-- = px | (px << 4);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // load custom font and selector (stored as 1st symbol in font table)\r
+       emu_make_path(buff, "skin/font.png", sizeof(buff));\r
+       readpng(menu_font_data, buff, READPNG_FONT,\r
+               MENU_X2 ? 256 : 128, MENU_X2 ? 320 : 160);\r
+       // default selector symbol is '>'\r
+       memcpy(menu_font_data, menu_font_data + ((int)'>') * me_mfont_w * me_mfont_h / 2,\r
+               me_mfont_w * me_mfont_h / 2);\r
+       emu_make_path(buff, "skin/selector.png", sizeof(buff));\r
+       readpng(menu_font_data, buff, READPNG_SELECTOR, me_mfont_w, me_mfont_h);\r
+\r
+       // load custom colors\r
+       emu_make_path(buff, "skin/skin.txt", sizeof(buff));\r
+       f = fopen(buff, "r");\r
+       if (f != NULL)\r
+       {\r
+               lprintf("found skin.txt\n");\r
+               while (!feof(f))\r
+               {\r
+                       if (fgets(buff, sizeof(buff), f) == NULL)\r
+                               break;\r
+                       if (buff[0] == '#'  || buff[0] == '/')  continue; // comment\r
+                       if (buff[0] == '\r' || buff[0] == '\n') continue; // empty line\r
+                       if (strncmp(buff, "text_color=", 11) == 0)\r
+                       {\r
+                               int tmp = parse_hex_color(buff+11);\r
+                               if (tmp >= 0) menu_text_color = tmp;\r
+                               else lprintf("skin.txt: parse error for text_color\n");\r
+                       }\r
+                       else if (strncmp(buff, "selection_color=", 16) == 0)\r
+                       {\r
+                               int tmp = parse_hex_color(buff+16);\r
+                               if (tmp >= 0) menu_sel_color = tmp;\r
+                               else lprintf("skin.txt: parse error for selection_color\n");\r
+                       }\r
+                       else\r
+                               lprintf("skin.txt: parse error: %s\n", buff);\r
+               }\r
+               fclose(f);\r
+       }\r
+}\r
+\r
+static void menu_draw_begin(int need_bg)\r
+{\r
+       plat_video_menu_begin();\r
+       if (need_bg)\r
+               memcpy(g_menuscreen_ptr, g_menubg_ptr, g_menuscreen_w * g_menuscreen_h * 2);\r
+}\r
+\r
+static void menu_draw_end(void)\r
+{\r
+       plat_video_menu_end();\r
+}\r
+\r
+static void menu_darken_bg(void *dst, void *src, int pixels, int darker)\r
+{\r
+       unsigned int *dest = dst;\r
+       unsigned int *sorc = src;\r
+       pixels /= 2;\r
+       if (darker)\r
+       {\r
+               while (pixels--)\r
+               {\r
+                       unsigned int p = *sorc++;\r
+                       *dest++ = ((p&0xf79ef79e)>>1) - ((p&0xc618c618)>>3);\r
+               }\r
+       }\r
+       else\r
+       {\r
+               while (pixels--)\r
+               {\r
+                       unsigned int p = *sorc++;\r
+                       *dest++ = (p&0xf79ef79e)>>1;\r
+               }\r
+       }\r
+}\r
+\r
+static void menu_enter(int is_rom_loaded)\r
+{\r
+       if (is_rom_loaded)\r
+       {\r
+               // darken the active framebuffer\r
+               menu_darken_bg(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h, 1);\r
+       }\r
+       else\r
+       {\r
+               char buff[256];\r
+\r
+               // should really only happen once, on startup..\r
+               emu_make_path(buff, "skin/background.png", sizeof(buff));\r
+               if (readpng(g_menubg_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h) < 0)\r
+                       memset(g_menubg_ptr, 0, g_menuscreen_w * g_menuscreen_h * 2);\r
+       }\r
+\r
+       plat_video_menu_enter(is_rom_loaded);\r
+}\r
+\r
+static int me_id2offset(const menu_entry *ent, menu_id id)\r
+{\r
+       int i;\r
+       for (i = 0; ent->name; ent++, i++)\r
+               if (ent->id == id) return i;\r
+\r
+       lprintf("%s: id %i not found\n", __FUNCTION__, id);\r
+       return 0;\r
+}\r
+\r
+static void me_enable(menu_entry *entries, menu_id id, int enable)\r
+{\r
+       int i = me_id2offset(entries, id);\r
+       entries[i].enabled = enable;\r
+}\r
+\r
+static int me_count(const menu_entry *ent)\r
+{\r
+       int ret;\r
+\r
+       for (ret = 0; ent->name; ent++, ret++)\r
+               ;\r
+\r
+       return ret;\r
+}\r
+\r
+static void me_draw(const menu_entry *entries, int sel, void (*draw_more)(void))\r
+{\r
+       const menu_entry *ent, *ent_sel = entries;\r
+       int x, y, w = 0, h = 0;\r
+       int offs, col2_offs = 27 * me_mfont_w;\r
+       int vi_sel_ln = 0;\r
+       const char *name;\r
+       int i, n;\r
+\r
+       /* calculate size of menu rect */\r
+       for (ent = entries, i = n = 0; ent->name; ent++, i++)\r
+       {\r
+               int wt;\r
+\r
+               if (!ent->enabled)\r
+                       continue;\r
+\r
+               if (i == sel) {\r
+                       ent_sel = ent;\r
+                       vi_sel_ln = n;\r
+               }\r
+\r
+               name = NULL;\r
+               wt = strlen(ent->name) * me_mfont_w;\r
+               if (wt == 0 && ent->generate_name)\r
+                       name = ent->generate_name(ent->id, &offs);\r
+               if (name != NULL)\r
+                       wt = strlen(name) * me_mfont_w;\r
+\r
+               if (ent->beh != MB_NONE)\r
+               {\r
+                       if (wt > col2_offs)\r
+                               col2_offs = wt + me_mfont_w;\r
+                       wt = col2_offs;\r
+\r
+                       switch (ent->beh) {\r
+                       case MB_NONE:\r
+                               break;\r
+                       case MB_OPT_ONOFF:\r
+                       case MB_OPT_RANGE:\r
+                               wt += me_mfont_w * 3;\r
+                               break;\r
+                       case MB_OPT_CUSTOM:\r
+                       case MB_OPT_CUSTONOFF:\r
+                       case MB_OPT_CUSTRANGE:\r
+                               name = NULL;\r
+                               offs = 0;\r
+                               if (ent->generate_name != NULL)\r
+                                       name = ent->generate_name(ent->id, &offs);\r
+                               if (name != NULL)\r
+                                       wt += (strlen(name) + offs) * me_mfont_w;\r
+                               break;\r
+                       case MB_OPT_ENUM:\r
+                               wt += 10 * me_mfont_w;\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               if (wt > w)\r
+                       w = wt;\r
+               n++;\r
+       }\r
+       h = n * me_mfont_h;\r
+       w += me_mfont_w * 2; /* selector */\r
+\r
+       if (w > g_menuscreen_w) {\r
+               lprintf("width %d > %d\n", w, g_menuscreen_w);\r
+               w = g_menuscreen_w;\r
+       }\r
+       if (h > g_menuscreen_h) {\r
+               lprintf("height %d > %d\n", w, g_menuscreen_h);\r
+               h = g_menuscreen_h;\r
+       }\r
+\r
+       x = g_menuscreen_w  / 2 - w / 2;\r
+       y = g_menuscreen_h / 2 - h / 2;\r
+\r
+       /* draw */\r
+       menu_draw_begin(1);\r
+       menu_draw_selection(x, y + vi_sel_ln * me_mfont_h, w);\r
+       x += me_mfont_w * 2;\r
+\r
+       for (ent = entries; ent->name; ent++)\r
+       {\r
+               const char **names;\r
+               int len;\r
+\r
+               if (!ent->enabled)\r
+                       continue;\r
+\r
+               name = ent->name;\r
+               if (strlen(name) == 0) {\r
+                       if (ent->generate_name)\r
+                               name = ent->generate_name(ent->id, &offs);\r
+               }\r
+               if (name != NULL)\r
+                       text_out16(x, y, name);\r
+\r
+               switch (ent->beh) {\r
+               case MB_NONE:\r
+                       break;\r
+               case MB_OPT_ONOFF:\r
+                       text_out16(x + col2_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");\r
+                       break;\r
+               case MB_OPT_RANGE:\r
+                       text_out16(x + col2_offs, y, "%i", *(int *)ent->var);\r
+                       break;\r
+               case MB_OPT_CUSTOM:\r
+               case MB_OPT_CUSTONOFF:\r
+               case MB_OPT_CUSTRANGE:\r
+                       name = NULL;\r
+                       offs = 0;\r
+                       if (ent->generate_name)\r
+                               name = ent->generate_name(ent->id, &offs);\r
+                       if (name != NULL)\r
+                               text_out16(x + col2_offs + offs * me_mfont_w, y, "%s", name);\r
+                       break;\r
+               case MB_OPT_ENUM:\r
+                       names = (const char **)ent->data;\r
+                       offs = 0;\r
+                       for (i = 0; names[i] != NULL; i++) {\r
+                               len = strlen(names[i]);\r
+                               if (len > 10)\r
+                                       offs = 10 - len - 2;\r
+                               if (i == *(int *)ent->var) {\r
+                                       text_out16(x + col2_offs + offs * me_mfont_w, y, "%s", names[i]);\r
+                                       break;\r
+                               }\r
+                       }\r
+                       break;\r
+               }\r
+\r
+               y += me_mfont_h;\r
+       }\r
+\r
+       /* display help or message if we have one */\r
+       h = (g_menuscreen_h - h) / 2; // bottom area height\r
+       if (menu_error_msg[0] != 0) {\r
+               if (h >= me_mfont_h + 4)\r
+                       text_out16(5, g_menuscreen_h - me_mfont_h - 4, menu_error_msg);\r
+               else\r
+                       lprintf("menu msg doesn't fit!\n");\r
+\r
+               if (plat_get_ticks_ms() - menu_error_time > 2048)\r
+                       menu_error_msg[0] = 0;\r
+       }\r
+       else if (ent_sel->help != NULL) {\r
+               const char *tmp = ent_sel->help;\r
+               int l;\r
+               for (l = 0; tmp != NULL && *tmp != 0; l++)\r
+                       tmp = strchr(tmp + 1, '\n');\r
+               if (h >= l * me_sfont_h + 4)\r
+                       for (tmp = ent_sel->help; l > 0; l--, tmp = strchr(tmp, '\n') + 1)\r
+                               smalltext_out16(5, g_menuscreen_h - (l * me_sfont_h + 4), tmp, 0xffff);\r
+       }\r
+\r
+       if (draw_more != NULL)\r
+               draw_more();\r
+\r
+       menu_draw_end();\r
+}\r
+\r
+static int me_process(menu_entry *entry, int is_next, int is_lr)\r
+{\r
+       const char **names;\r
+       int c;\r
+       switch (entry->beh)\r
+       {\r
+               case MB_OPT_ONOFF:\r
+               case MB_OPT_CUSTONOFF:\r
+                       *(int *)entry->var ^= entry->mask;\r
+                       return 1;\r
+               case MB_OPT_RANGE:\r
+               case MB_OPT_CUSTRANGE:\r
+                       c = is_lr ? 10 : 1;\r
+                       *(int *)entry->var += is_next ? c : -c;\r
+                       if (*(int *)entry->var < (int)entry->min)\r
+                               *(int *)entry->var = (int)entry->max;\r
+                       if (*(int *)entry->var > (int)entry->max)\r
+                               *(int *)entry->var = (int)entry->min;\r
+                       return 1;\r
+               case MB_OPT_ENUM:\r
+                       names = (const char **)entry->data;\r
+                       for (c = 0; names[c] != NULL; c++)\r
+                               ;\r
+                       *(int *)entry->var += is_next ? 1 : -1;\r
+                       if (*(int *)entry->var < 0)\r
+                               *(int *)entry->var = 0;\r
+                       if (*(int *)entry->var >= c)\r
+                               *(int *)entry->var = c - 1;\r
+                       return 1;\r
+               default:\r
+                       return 0;\r
+       }\r
+}\r
+\r
+static void debug_menu_loop(void);\r
+\r
+static void me_loop(menu_entry *menu, int *menu_sel, void (*draw_more)(void))\r
+{\r
+       int ret, inp, sel = *menu_sel, menu_sel_max;\r
+\r
+       menu_sel_max = me_count(menu) - 1;\r
+       if (menu_sel_max < 0) {\r
+               lprintf("no enabled menu entries\n");\r
+               return;\r
+       }\r
+\r
+       while ((!menu[sel].enabled || !menu[sel].selectable) && sel < menu_sel_max)\r
+               sel++;\r
+\r
+       /* make sure action buttons are not pressed on entering menu */\r
+       me_draw(menu, sel, NULL);\r
+       while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU));\r
+\r
+       for (;;)\r
+       {\r
+               me_draw(menu, sel, draw_more);\r
+               inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|\r
+                                       PBTN_MOK|PBTN_MBACK|PBTN_MENU|PBTN_L|PBTN_R, 70);\r
+               if (inp & (PBTN_MENU|PBTN_MBACK))\r
+                       break;\r
+\r
+               if (inp & PBTN_UP  ) {\r
+                       do {\r
+                               sel--;\r
+                               if (sel < 0)\r
+                                       sel = menu_sel_max;\r
+                       }\r
+                       while (!menu[sel].enabled || !menu[sel].selectable);\r
+               }\r
+               if (inp & PBTN_DOWN) {\r
+                       do {\r
+                               sel++;\r
+                               if (sel > menu_sel_max)\r
+                                       sel = 0;\r
+                       }\r
+                       while (!menu[sel].enabled || !menu[sel].selectable);\r
+               }\r
+\r
+               /* a bit hacky but oh well */\r
+               if ((inp & (PBTN_L|PBTN_R)) == (PBTN_L|PBTN_R))\r
+                       debug_menu_loop();\r
+\r
+               if (inp & (PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R)) { /* multi choice */\r
+                       if (me_process(&menu[sel], (inp & (PBTN_RIGHT|PBTN_R)) ? 1 : 0,\r
+                                               inp & (PBTN_L|PBTN_R)))\r
+                               continue;\r
+               }\r
+\r
+               if (inp & (PBTN_MOK|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R))\r
+               {\r
+                       /* require PBTN_MOK for MB_NONE */\r
+                       if (menu[sel].handler != NULL && (menu[sel].beh != MB_NONE || (inp & PBTN_MOK))) {\r
+                               ret = menu[sel].handler(menu[sel].id, inp);\r
+                               if (ret) break;\r
+                               menu_sel_max = me_count(menu) - 1; /* might change, so update */\r
+                       }\r
+               }\r
+       }\r
+       *menu_sel = sel;\r
+}\r
+\r
+/* ***************************************** */\r
+\r
+static void draw_menu_credits(void)\r
+{\r
+       const char *creds, *p;\r
+       int x, y, h, w, wt;\r
+\r
+       p = creds = plat_get_credits();\r
+\r
+       for (h = 1, w = 0; *p != 0; h++) {\r
+               for (wt = 0; *p != 0 && *p != '\n'; p++)\r
+                       wt++;\r
+\r
+               if (wt > w)\r
+                       w = wt;\r
+               if (*p == 0)\r
+                       break;\r
+               p++;\r
+       }\r
+\r
+       x = g_menuscreen_w  / 2 - w * me_mfont_w / 2;\r
+       y = g_menuscreen_h / 2 - h * me_mfont_h / 2;\r
+       if (x < 0) x = 0;\r
+       if (y < 0) y = 0;\r
+\r
+       menu_draw_begin(1);\r
+\r
+       for (p = creds; *p != 0 && y <= g_menuscreen_h - me_mfont_h; y += me_mfont_h) {\r
+               text_out16(x, y, p);\r
+\r
+               for (; *p != 0 && *p != '\n'; p++)\r
+                       ;\r
+               if (*p != 0)\r
+                       p++;\r
+       }\r
+\r
+       menu_draw_end();\r
+}\r
+\r
+// -------------- del confirm ---------------\r
+\r
+static void do_delete(const char *fpath, const char *fname)\r
+{\r
+       int len, mid, inp;\r
+       const char *nm;\r
+       char tmp[64];\r
+\r
+       menu_draw_begin(1);\r
+\r
+       len = strlen(fname);\r
+       if (len > g_menuscreen_w / me_sfont_w)\r
+               len = g_menuscreen_w / me_sfont_w;\r
+\r
+       mid = g_menuscreen_w / 2;\r
+       text_out16(mid - me_mfont_w * 15 / 2,  8 * me_mfont_h, "About to delete");\r
+       smalltext_out16(mid - len * me_sfont_w / 2, 9 * me_mfont_h + 5, fname, 0xbdff);\r
+       text_out16(mid - me_mfont_w * 13 / 2, 11 * me_mfont_h, "Are you sure?");\r
+\r
+       nm = in_get_key_name(-1, -PBTN_MA3);\r
+       snprintf(tmp, sizeof(tmp), "(%s - confirm, ", nm);\r
+       len = strlen(tmp);\r
+       nm = in_get_key_name(-1, -PBTN_MBACK);\r
+       snprintf(tmp + len, sizeof(tmp) - len, "%s - cancel)", nm);\r
+       len = strlen(tmp);\r
+\r
+       text_out16(mid - me_mfont_w * len / 2, 12 * me_mfont_h, tmp);\r
+       menu_draw_end();\r
+\r
+       while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MA2));\r
+       inp = in_menu_wait(PBTN_MA3|PBTN_MBACK, 100);\r
+       if (inp & PBTN_MA3)\r
+               remove(fpath);\r
+}\r
+\r
+// -------------- ROM selector --------------\r
+\r
+// rrrr rggg gggb bbbb\r
+static unsigned short file2color(const char *fname)\r
+{\r
+       const char *ext = fname + strlen(fname) - 3;\r
+       static const char *rom_exts[]   = { "zip", "bin", "smd", "gen", "iso", "cso", "cue" };\r
+       static const char *other_exts[] = { "gmv", "pat" };\r
+       int i;\r
+\r
+       if (ext < fname) ext = fname;\r
+       for (i = 0; i < array_size(rom_exts); i++)\r
+               if (strcasecmp(ext, rom_exts[i]) == 0) return 0xbdff; // FIXME: mk defines\r
+       for (i = 0; i < array_size(other_exts); i++)\r
+               if (strcasecmp(ext, other_exts[i]) == 0) return 0xaff5;\r
+       return 0xffff;\r
+}\r
+\r
+static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)\r
+{\r
+       int max_cnt, start, i, x, pos;\r
+       void *darken_ptr;\r
+\r
+       max_cnt = g_menuscreen_h / me_sfont_h;\r
+       start = max_cnt / 2 - sel;\r
+       n--; // exclude current dir (".")\r
+\r
+       menu_draw_begin(1);\r
+\r
+//     if (!rom_loaded)\r
+//             menu_darken_bg(gp2x_screen, 320*240, 0);\r
+\r
+       darken_ptr = (short *)g_menuscreen_ptr + g_menuscreen_w * max_cnt/2 * me_sfont_h;\r
+       menu_darken_bg(darken_ptr, darken_ptr, g_menuscreen_w * me_sfont_h * 8 / 10, 0);\r
+\r
+       x = 5 + me_mfont_w + 1;\r
+       if (start - 2 >= 0)\r
+               smalltext_out16(14, (start - 2) * me_sfont_h, curdir, 0xffff);\r
+       for (i = 0; i < n; i++) {\r
+               pos = start + i;\r
+               if (pos < 0)  continue;\r
+               if (pos >= max_cnt) break;\r
+               if (namelist[i+1]->d_type == DT_DIR) {\r
+                       smalltext_out16(x, pos * me_sfont_h, "/", 0xfff6);\r
+                       smalltext_out16(x + me_sfont_w, pos * me_sfont_h, namelist[i+1]->d_name, 0xfff6);\r
+               } else {\r
+                       unsigned short color = file2color(namelist[i+1]->d_name);\r
+                       smalltext_out16(x, pos * me_sfont_h, namelist[i+1]->d_name, color);\r
+               }\r
+       }\r
+       smalltext_out16(5, max_cnt/2 * me_sfont_h, ">", 0xffff);\r
+       menu_draw_end();\r
+}\r
+\r
+static int scandir_cmp(const void *p1, const void *p2)\r
+{\r
+       const struct dirent **d1 = (const struct dirent **)p1;\r
+       const struct dirent **d2 = (const struct dirent **)p2;\r
+       if ((*d1)->d_type == (*d2)->d_type)\r
+               return alphasort(d1, d2);\r
+       if ((*d1)->d_type == DT_DIR)\r
+               return -1; // put before\r
+       if ((*d2)->d_type == DT_DIR)\r
+               return  1;\r
+\r
+       return alphasort(d1, d2);\r
+}\r
+\r
+static const char *filter_exts[] = {\r
+       ".mp3", ".MP3", ".srm", ".brm", "s.gz", ".mds", "bcfg", ".txt", ".htm", "html",\r
+       ".jpg", ".gpe"\r
+};\r
+\r
+static int scandir_filter(const struct dirent *ent)\r
+{\r
+       const char *p;\r
+       int i;\r
+\r
+       if (ent == NULL || ent->d_name == NULL) return 0;\r
+       if (strlen(ent->d_name) < 5) return 1;\r
+\r
+       p = ent->d_name + strlen(ent->d_name) - 4;\r
+\r
+       for (i = 0; i < array_size(filter_exts); i++)\r
+               if (strcmp(p, filter_exts[i]) == 0)\r
+                       return 0;\r
+\r
+       return 1;\r
+}\r
+\r
+static char *menu_loop_romsel(char *curr_path, int len)\r
+{\r
+       struct dirent **namelist;\r
+       int n, inp, sel = 0;\r
+       char *ret = NULL, *fname = NULL;\r
+\r
+rescan:\r
+       // is this a dir or a full path?\r
+       if (!plat_is_dir(curr_path)) {\r
+               char *p = curr_path + strlen(curr_path) - 1;\r
+               for (; p > curr_path && *p != '/'; p--)\r
+                       ;\r
+               *p = 0;\r
+               fname = p+1;\r
+       }\r
+\r
+       n = scandir(curr_path, &namelist, scandir_filter, (void *)scandir_cmp);\r
+       if (n < 0) {\r
+               char *t;\r
+               lprintf("menu_loop_romsel failed, dir: %s\n", curr_path);\r
+\r
+               // try root\r
+               t = getcwd(curr_path, len);\r
+               if (t == NULL)\r
+                       plat_get_root_dir(curr_path, len);\r
+               n = scandir(curr_path, &namelist, scandir_filter, (void *)scandir_cmp);\r
+               if (n < 0) {\r
+                       // oops, we failed\r
+                       lprintf("menu_loop_romsel failed, dir: %s\n", curr_path);\r
+                       return NULL;\r
+               }\r
+       }\r
+\r
+       // try to find sel\r
+       if (fname != NULL) {\r
+               int i;\r
+               for (i = 1; i < n; i++) {\r
+                       if (strcmp(namelist[i]->d_name, fname) == 0) {\r
+                               sel = i - 1;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       /* make sure action buttons are not pressed on entering menu */\r
+       draw_dirlist(curr_path, namelist, n, sel);\r
+       while (in_menu_wait_any(50) & (PBTN_MOK|PBTN_MBACK|PBTN_MENU))\r
+               ;\r
+\r
+       for (;;)\r
+       {\r
+               draw_dirlist(curr_path, namelist, n, sel);\r
+               inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|\r
+                       PBTN_L|PBTN_R|PBTN_MA2|PBTN_MOK|PBTN_MBACK|PBTN_MENU, 33);\r
+               if (inp & PBTN_UP  )  { sel--;   if (sel < 0)   sel = n-2; }\r
+               if (inp & PBTN_DOWN)  { sel++;   if (sel > n-2) sel = 0; }\r
+               if (inp & PBTN_LEFT)  { sel-=10; if (sel < 0)   sel = 0; }\r
+               if (inp & PBTN_L)     { sel-=24; if (sel < 0)   sel = 0; }\r
+               if (inp & PBTN_RIGHT) { sel+=10; if (sel > n-2) sel = n-2; }\r
+               if (inp & PBTN_R)     { sel+=24; if (sel > n-2) sel = n-2; }\r
+               if ((inp & PBTN_MOK) || (inp & (PBTN_MENU|PBTN_MA2)) == (PBTN_MENU|PBTN_MA2))\r
+               {\r
+                       again:\r
+                       if (namelist[sel+1]->d_type == DT_REG)\r
+                       {\r
+                               strcpy(rom_fname_reload, curr_path);\r
+                               strcat(rom_fname_reload, "/");\r
+                               strcat(rom_fname_reload, namelist[sel+1]->d_name);\r
+                               if (inp & PBTN_MOK) { // return sel\r
+                                       ret = rom_fname_reload;\r
+                                       break;\r
+                               }\r
+                               do_delete(rom_fname_reload, namelist[sel+1]->d_name);\r
+                               if (n > 0) {\r
+                                       while (n--) free(namelist[n]);\r
+                                       free(namelist);\r
+                               }\r
+                               goto rescan;\r
+                       }\r
+                       else if (namelist[sel+1]->d_type == DT_DIR)\r
+                       {\r
+                               int newlen;\r
+                               char *p, *newdir;\r
+                               if (!(inp & PBTN_MOK)) continue;\r
+                               newlen = strlen(curr_path) + strlen(namelist[sel+1]->d_name) + 2;\r
+                               newdir = malloc(newlen);\r
+                               if (strcmp(namelist[sel+1]->d_name, "..") == 0) {\r
+                                       char *start = curr_path;\r
+                                       p = start + strlen(start) - 1;\r
+                                       while (*p == '/' && p > start) p--;\r
+                                       while (*p != '/' && p > start) p--;\r
+                                       if (p <= start) strcpy(newdir, "/");\r
+                                       else { strncpy(newdir, start, p-start); newdir[p-start] = 0; }\r
+                               } else {\r
+                                       strcpy(newdir, curr_path);\r
+                                       p = newdir + strlen(newdir) - 1;\r
+                                       while (*p == '/' && p >= newdir) *p-- = 0;\r
+                                       strcat(newdir, "/");\r
+                                       strcat(newdir, namelist[sel+1]->d_name);\r
+                               }\r
+                               ret = menu_loop_romsel(newdir, newlen);\r
+                               free(newdir);\r
+                               break;\r
+                       }\r
+                       else\r
+                       {\r
+                               // unknown file type, happens on NTFS mounts. Try to guess.\r
+                               FILE *tstf; int tmp;\r
+                               strcpy(rom_fname_reload, curr_path);\r
+                               strcat(rom_fname_reload, "/");\r
+                               strcat(rom_fname_reload, namelist[sel+1]->d_name);\r
+                               tstf = fopen(rom_fname_reload, "rb");\r
+                               if (tstf != NULL)\r
+                               {\r
+                                       if (fread(&tmp, 1, 1, tstf) > 0 || ferror(tstf) == 0)\r
+                                               namelist[sel+1]->d_type = DT_REG;\r
+                                       else    namelist[sel+1]->d_type = DT_DIR;\r
+                                       fclose(tstf);\r
+                                       goto again;\r
+                               }\r
+                       }\r
+               }\r
+               if (inp & PBTN_MBACK)\r
+                       break;\r
+       }\r
+\r
+       if (n > 0) {\r
+               while (n--) free(namelist[n]);\r
+               free(namelist);\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// ------------ savestate loader ------------\r
+\r
+static int state_slot_flags = 0;\r
+\r
+static void state_check_slots(void)\r
+{\r
+       int slot;\r
+\r
+       state_slot_flags = 0;\r
+\r
+       for (slot = 0; slot < 10; slot++) {\r
+               if (emu_check_save_file(slot))\r
+                       state_slot_flags |= 1 << slot;\r
+       }\r
+}\r
+\r
+static void draw_savestate_bg(int slot);\r
+\r
+static void draw_savestate_menu(int menu_sel, int is_loading)\r
+{\r
+       int i, x, y, w, h;\r
+\r
+       if (state_slot_flags & (1 << menu_sel))\r
+               draw_savestate_bg(menu_sel);\r
+\r
+       w = (13 + 2) * me_mfont_w;\r
+       h = (1+2+10+1) * me_mfont_h;\r
+       x = g_menuscreen_w / 2 - w / 2;\r
+       if (x < 0) x = 0;\r
+       y = g_menuscreen_h / 2 - h / 2;\r
+       if (y < 0) y = 0;\r
+\r
+       menu_draw_begin(1);\r
+\r
+       text_out16(x, y, is_loading ? "Load state" : "Save state");\r
+       y += 3 * me_mfont_h;\r
+\r
+       menu_draw_selection(x - me_mfont_w * 2, y + menu_sel * me_mfont_h, (13 + 2) * me_mfont_w + 4);\r
+\r
+       /* draw all 10 slots */\r
+       for (i = 0; i < 10; i++, y += me_mfont_h)\r
+       {\r
+               text_out16(x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");\r
+       }\r
+       text_out16(x, y, "back");\r
+\r
+       menu_draw_end();\r
+}\r
+\r
+static int menu_loop_savestate(int is_loading)\r
+{\r
+       static int menu_sel = 10;\r
+       int menu_sel_max = 10;\r
+       unsigned long inp = 0;\r
+       int ret = 0;\r
+\r
+       state_check_slots();\r
+\r
+       if (!(state_slot_flags & (1 << menu_sel)) && is_loading)\r
+               menu_sel = menu_sel_max;\r
+\r
+       for (;;)\r
+       {\r
+               draw_savestate_menu(menu_sel, is_loading);\r
+               inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_MOK|PBTN_MBACK, 100);\r
+               if (inp & PBTN_UP) {\r
+                       do {\r
+                               menu_sel--;\r
+                               if (menu_sel < 0)\r
+                                       menu_sel = menu_sel_max;\r
+                       } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
+               }\r
+               if (inp & PBTN_DOWN) {\r
+                       do {\r
+                               menu_sel++;\r
+                               if (menu_sel > menu_sel_max)\r
+                                       menu_sel = 0;\r
+                       } while (!(state_slot_flags & (1 << menu_sel)) && menu_sel != menu_sel_max && is_loading);\r
+               }\r
+               if (inp & PBTN_MOK) { // save/load\r
+                       if (menu_sel < 10) {\r
+                               state_slot = menu_sel;\r
+                               if (emu_save_load_game(is_loading, 0)) {\r
+                                       me_update_msg(is_loading ? "Load failed" : "Save failed");\r
+                                       break;\r
+                               }\r
+                               ret = 1;\r
+                               break;\r
+                       }\r
+                       break;\r
+               }\r
+               if (inp & PBTN_MBACK)\r
+                       break;\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+// -------------- key config --------------\r
+\r
+static char *action_binds(int player_idx, int action_mask, int dev_id)\r
+{\r
+       int k, count = 0, can_combo = 0, type;\r
+       const int *binds;\r
+\r
+       static_buff[0] = 0;\r
+\r
+       binds = in_get_dev_binds(dev_id);\r
+       if (binds == NULL)\r
+               return static_buff;\r
+\r
+       in_get_config(dev_id, IN_CFG_BIND_COUNT, &count);\r
+       in_get_config(dev_id, IN_CFG_DOES_COMBOS, &can_combo);\r
+\r
+       type = IN_BINDTYPE_EMU;\r
+       if (player_idx >= 0) {\r
+               can_combo = 0;\r
+               type = IN_BINDTYPE_PLAYER12;\r
+       }\r
+       if (player_idx == 1)\r
+               action_mask <<= 16;\r
+\r
+       for (k = 0; k < count; k++)\r
+       {\r
+               const char *xname;\r
+               int len;\r
+\r
+               if (!(binds[IN_BIND_OFFS(k, type)] & action_mask))\r
+                       continue;\r
+\r
+               xname = in_get_key_name(dev_id, k);\r
+               len = strlen(static_buff);\r
+               if (len) {\r
+                       strncat(static_buff, can_combo ? " + " : ", ",\r
+                               sizeof(static_buff) - len - 1);\r
+                       len += can_combo ? 3 : 2;\r
+               }\r
+               strncat(static_buff, xname, sizeof(static_buff) - len - 1);\r
+       }\r
+\r
+       return static_buff;\r
+}\r
+\r
+static int count_bound_keys(int dev_id, int action_mask, int bindtype)\r
+{\r
+       const int *binds;\r
+       int k, keys = 0;\r
+       int count = 0;\r
+\r
+       binds = in_get_dev_binds(dev_id);\r
+       if (binds == NULL)\r
+               return 0;\r
+\r
+       in_get_config(dev_id, IN_CFG_BIND_COUNT, &count);\r
+       for (k = 0; k < count; k++)\r
+       {\r
+               if (binds[IN_BIND_OFFS(k, bindtype)] & action_mask)\r
+                       keys++;\r
+       }\r
+\r
+       return keys;\r
+}\r
+\r
+static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx,\r
+               int sel, int dev_id, int dev_count, int is_bind)\r
+{\r
+       char buff[64], buff2[32];\r
+       const char *dev_name;\r
+       int x, y, w, i;\r
+\r
+       w = ((player_idx >= 0) ? 20 : 30) * me_mfont_w;\r
+       x = g_menuscreen_w / 2 - w / 2;\r
+       y = (g_menuscreen_h - 4 * me_mfont_h) / 2 - (2 + opt_cnt) * me_mfont_h / 2;\r
+       if (x < me_mfont_w * 2)\r
+               x = me_mfont_w * 2;\r
+\r
+       menu_draw_begin(1);\r
+       if (player_idx >= 0)\r
+               text_out16(x, y, "Player %i controls", player_idx + 1);\r
+       else\r
+               text_out16(x, y, "Emulator controls");\r
+\r
+       y += 2 * me_mfont_h;\r
+       menu_draw_selection(x - me_mfont_w * 2, y + sel * me_mfont_h, w + 2 * me_mfont_w);\r
+\r
+       for (i = 0; i < opt_cnt; i++, y += me_mfont_h)\r
+               text_out16(x, y, "%s : %s", opts[i].name,\r
+                       action_binds(player_idx, opts[i].mask, dev_id));\r
+\r
+       dev_name = in_get_dev_name(dev_id, 1, 1);\r
+       w = strlen(dev_name) * me_mfont_w;\r
+       if (w < 30 * me_mfont_w)\r
+               w = 30 * me_mfont_w;\r
+       if (w > g_menuscreen_w)\r
+               w = g_menuscreen_w;\r
+\r
+       x = g_menuscreen_w / 2 - w / 2;\r
+\r
+       if (!is_bind) {\r
+               snprintf(buff2, sizeof(buff2), "%s", in_get_key_name(-1, -PBTN_MOK));\r
+               snprintf(buff, sizeof(buff), "%s - bind, %s - clear", buff2,\r
+                               in_get_key_name(-1, -PBTN_MA2));\r
+               text_out16(x, g_menuscreen_h - 4 * me_mfont_h, buff);\r
+       }\r
+       else\r
+               text_out16(x, g_menuscreen_h - 4 * me_mfont_h, "Press a button to bind/unbind");\r
+\r
+       if (dev_count > 1) {\r
+               text_out16(x, g_menuscreen_h - 3 * me_mfont_h, dev_name);\r
+               text_out16(x, g_menuscreen_h - 2 * me_mfont_h, "Press left/right for other devs");\r
+       }\r
+\r
+       menu_draw_end();\r
+}\r
+\r
+static void key_config_loop(const me_bind_action *opts, int opt_cnt, int player_idx)\r
+{\r
+       int i, sel = 0, menu_sel_max = opt_cnt - 1, does_combos = 0;\r
+       int dev_id, dev_count, kc, is_down, mkey;\r
+       int unbind, bindtype, mask_shift;\r
+\r
+       for (i = 0, dev_id = -1, dev_count = 0; i < IN_MAX_DEVS; i++) {\r
+               if (in_get_dev_name(i, 1, 0) != NULL) {\r
+                       dev_count++;\r
+                       if (dev_id < 0)\r
+                               dev_id = i;\r
+               }\r
+       }\r
+\r
+       if (dev_id == -1) {\r
+               lprintf("no devs, can't do config\n");\r
+               return;\r
+       }\r
+\r
+       mask_shift = 0;\r
+       if (player_idx == 1)\r
+               mask_shift = 16;\r
+       bindtype = player_idx >= 0 ? IN_BINDTYPE_PLAYER12 : IN_BINDTYPE_EMU;\r
+\r
+       for (;;)\r
+       {\r
+               draw_key_config(opts, opt_cnt, player_idx, sel, dev_id, dev_count, 0);\r
+               mkey = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_MBACK|PBTN_MOK|PBTN_MA2, 100);\r
+               switch (mkey) {\r
+                       case PBTN_UP:   sel--; if (sel < 0) sel = menu_sel_max; continue;\r
+                       case PBTN_DOWN: sel++; if (sel > menu_sel_max) sel = 0; continue;\r
+                       case PBTN_LEFT:\r
+                               for (i = 0, dev_id--; i < IN_MAX_DEVS; i++, dev_id--) {\r
+                                       if (dev_id < 0)\r
+                                               dev_id = IN_MAX_DEVS - 1;\r
+                                       if (in_get_dev_name(dev_id, 1, 0) != NULL)\r
+                                               break;\r
+                               }\r
+                               continue;\r
+                       case PBTN_RIGHT:\r
+                               for (i = 0, dev_id++; i < IN_MAX_DEVS; i++, dev_id++) {\r
+                                       if (dev_id >= IN_MAX_DEVS)\r
+                                               dev_id = 0;\r
+                                       if (in_get_dev_name(dev_id, 1, 0) != NULL)\r
+                                               break;\r
+                               }\r
+                               continue;\r
+                       case PBTN_MBACK:\r
+                               return;\r
+                       case PBTN_MOK:\r
+                               if (sel >= opt_cnt)\r
+                                       return;\r
+                               while (in_menu_wait_any(30) & PBTN_MOK)\r
+                                       ;\r
+                               break;\r
+                       case PBTN_MA2:\r
+                               in_unbind_all(dev_id, opts[sel].mask << mask_shift, bindtype);\r
+                               continue;\r
+                       default:continue;\r
+               }\r
+\r
+               draw_key_config(opts, opt_cnt, player_idx, sel, dev_id, dev_count, 1);\r
+\r
+               /* wait for some up event */\r
+               for (is_down = 1; is_down; )\r
+                       kc = in_update_keycode(&dev_id, &is_down, -1);\r
+\r
+               i = count_bound_keys(dev_id, opts[sel].mask << mask_shift, bindtype);\r
+               unbind = (i > 0);\r
+\r
+               /* allow combos if device supports them */\r
+               in_get_config(dev_id, IN_CFG_DOES_COMBOS, &does_combos);\r
+               if (i == 1 && bindtype == IN_BINDTYPE_EMU && does_combos)\r
+                       unbind = 0;\r
+\r
+               if (unbind)\r
+                       in_unbind_all(dev_id, opts[sel].mask << mask_shift, bindtype);\r
+\r
+               in_bind_key(dev_id, kc, opts[sel].mask << mask_shift, bindtype, 0);\r
+       }\r
+}\r
+\r
diff --git a/frontend/common/menu.h b/frontend/common/menu.h
new file mode 100644 (file)
index 0000000..21c589e
--- /dev/null
@@ -0,0 +1,115 @@
+// (c) Copyright 2006-2009 notaz, All rights reserved.
+
+typedef enum
+{
+       MB_NONE = 1,            /* no auto processing */
+       MB_OPT_ONOFF,           /* ON/OFF setting */
+       MB_OPT_RANGE,           /* [min-max] setting */
+       MB_OPT_CUSTOM,          /* custom value */
+       MB_OPT_CUSTONOFF,
+       MB_OPT_CUSTRANGE,
+       MB_OPT_ENUM,
+} menu_behavior;
+
+typedef struct
+{
+       const char *name;
+       menu_behavior beh;
+       int id;
+       void *var;              /* for on-off/range settings */
+       int mask;               /* bit to toggle for on/off */
+       signed short min;       /* for ranged integer settings, to be sign-extended */
+       signed short max;
+       unsigned int enabled:1;
+       unsigned int need_to_save:1;
+       unsigned int selectable:1;
+       int (*handler)(int id, int keys);
+       const char * (*generate_name)(int id, int *offs);
+       const void *data;
+       const char *help;
+} menu_entry;
+
+#define mee_handler_id(name, id, handler) \
+       { name, MB_NONE, id, NULL, 0, 0, 0, 1, 0, 1, handler, NULL, NULL, NULL }
+
+#define mee_handler(name, handler) \
+       mee_handler_id(name, MA_NONE, handler)
+
+#define mee_label(name) \
+       { name, MB_NONE, MA_NONE, NULL, 0, 0, 0, 1, 0, 0, NULL, NULL, NULL, NULL }
+
+#define mee_label_mk(id, name_func) \
+       { "", MB_NONE, id, NULL, 0, 0, 0, 1, 0, 0, NULL, name_func, NULL, NULL }
+
+#define mee_onoff_h(name, id, var, mask, help) \
+       { name, MB_OPT_ONOFF, id, &(var), mask, 0, 0, 1, 1, 1, NULL, NULL, NULL, help }
+
+#define mee_onoff(name, id, var, mask) \
+       mee_onoff_h(name, id, var, mask, NULL)
+
+#define mee_range(name, id, var, min, max) \
+       { name, MB_OPT_RANGE, id, &(var), 0, min, max, 1, 1, 1, NULL, NULL, NULL, NULL }
+
+#define mee_range_hide(name, id, var, min, max) \
+       { name, MB_OPT_RANGE, id, &(var), 0, min, max, 0, 1, 0, NULL, NULL, NULL, NULL }
+
+#define mee_cust_s_h(name, id, need_save, handler, name_func, help) \
+       { name, MB_OPT_CUSTOM, id, NULL, 0, 0, 0, 1, need_save, 1, handler, name_func, NULL, help }
+
+#define mee_cust_h(name, id, handler, name_func, help) \
+       mee_cust_s_h(name, id, 1, handler, name_func, help)
+
+#define mee_cust(name, id, handler, name_func) \
+       mee_cust_h(name, id, handler, name_func, NULL)
+
+#define mee_cust_nosave(name, id, handler, name_func) \
+       mee_cust_s_h(name, id, 0, handler, name_func, NULL)
+
+#define mee_onoff_cust(name, id, var, mask, name_func) \
+       { name, MB_OPT_CUSTONOFF, id, &(var), mask, 0, 0, 1, 1, 1, NULL, name_func, NULL, NULL }
+
+#define mee_range_cust(name, id, var, min, max, name_func) \
+       { name, MB_OPT_CUSTRANGE, id, &(var), 0, min, max, 1, 1, 1, NULL, name_func, NULL, NULL }
+
+#define mee_enum_h(name, id, var, names_list, help) \
+       { name, MB_OPT_ENUM, id, &(var), 0, 0, 0, 1, 1, 1, NULL, NULL, names_list, help }
+
+#define mee_enum(name, id, var, names_list) \
+       mee_enum_h(name, id, var, names_list, NULL)
+
+#define mee_end \
+       { NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
+
+typedef struct
+{
+       char *name;
+       int mask;
+} me_bind_action;
+
+extern me_bind_action me_ctrl_actions[];
+extern me_bind_action emuctrl_actions[];       // platform code
+
+extern void *g_menubg_src_ptr;
+extern void *g_menubg_ptr;
+extern void *g_menuscreen_ptr;
+#if MSCREEN_SIZE_FIXED
+#define g_menuscreen_w MSCREEN_WIDTH
+#define g_menuscreen_h MSCREEN_HEIGHT
+#else
+extern int g_menuscreen_w;
+extern int g_menuscreen_h;
+#endif
+
+void menu_init(void);
+void text_out16(int x, int y, const char *texto, ...);
+void me_update_msg(const char *msg);
+
+void menu_romload_prepare(const char *rom_name);
+void menu_romload_end(void);
+
+void menu_loop(void);
+int  menu_loop_tray(void);
+
+menu_entry *me_list_get_first(void);
+menu_entry *me_list_get_next(void);
+
diff --git a/frontend/common/plat.h b/frontend/common/plat.h
new file mode 100644 (file)
index 0000000..c16f888
--- /dev/null
@@ -0,0 +1,58 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* stuff to be implemented by platform code */
+extern const char *renderer_names[];
+extern const char *renderer_names32x[];
+
+void pemu_prep_defconfig(void);
+void pemu_validate_config(void);
+void pemu_loop_prep(void);
+void pemu_loop_end(void);
+void pemu_forced_frame(int no_scale, int do_emu); // ..to g_menubg_src_ptr
+void pemu_finalize_frame(const char *fps, const char *notice_msg);
+
+void pemu_sound_start(void);
+void pemu_sound_stop(void);
+void pemu_sound_wait(void);
+
+void plat_early_init(void);
+void plat_init(void);
+void plat_finish(void);
+
+/* return the dir/ where configs, saves, bios, etc. are found */
+int  plat_get_root_dir(char *dst, int len);
+
+/* used before things blocking for a while (these funcs redraw on return) */
+void plat_status_msg_busy_first(const char *msg);
+void plat_status_msg_busy_next(const char *msg);
+void plat_status_msg_clear(void);
+
+/* menu: enter (switch bpp, etc), begin/end drawing */
+void plat_video_menu_enter(int is_rom_loaded);
+void plat_video_menu_begin(void);
+void plat_video_menu_end(void);
+
+void plat_video_flip(void);
+void plat_video_wait_vsync(void);
+void plat_video_toggle_renderer(int change, int menu_call);
+
+void plat_update_volume(int has_changed, int is_up);
+
+int  plat_is_dir(const char *path);
+int  plat_wait_event(int *fds_hnds, int count, int timeout_ms);
+void plat_sleep_ms(int ms);
+
+/* timers, to be used for time diff and must refer to the same clock */
+unsigned int plat_get_ticks_ms(void);
+unsigned int plat_get_ticks_us(void);
+void plat_wait_till_us(unsigned int us);
+
+const char  *plat_get_credits(void);
+void plat_debug_cat(char *str);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
diff --git a/frontend/common/posix.h b/frontend/common/posix.h
new file mode 100644 (file)
index 0000000..33ca96c
--- /dev/null
@@ -0,0 +1,23 @@
+/* define POSIX stuff: dirent, scandir, getcwd, mkdir */
+#if defined(__linux__) || defined(__MINGW32__)
+
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef __MINGW32__
+#warning hacks!
+#define mkdir(pathname,mode) mkdir(pathname)
+#define d_type d_ino
+#define DT_REG 0
+#define DT_DIR 0
+#endif
+
+#else
+
+#error "must provide posix"
+
+#endif
+
+
diff --git a/frontend/common/readpng.c b/frontend/common/readpng.c
new file mode 100644 (file)
index 0000000..b7bad57
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * (C) Gražvydas "notaz" Ignotas, 2008-2010
+ *
+ * This work is licensed under the terms of any of these licenses
+ * (at your option):
+ *  - GNU GPL, version 2 or later.
+ *  - GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <png.h>
+#include "readpng.h"
+#include "lprintf.h"
+
+int readpng(void *dest, const char *fname, readpng_what what, int req_w, int req_h)
+{
+       FILE *fp;
+       png_structp png_ptr = NULL;
+       png_infop info_ptr = NULL;
+       png_bytepp row_ptr = NULL;
+       int ret = -1;
+
+       if (dest == NULL || fname == NULL)
+       {
+               return -1;
+       }
+
+       fp = fopen(fname, "rb");
+       if (fp == NULL)
+       {
+               lprintf(__FILE__ ": failed to open: %s\n", fname);
+               return -1;
+       }
+
+       png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+       if (!png_ptr)
+       {
+               lprintf(__FILE__ ": png_create_read_struct() failed\n");
+               fclose(fp);
+               return -1;
+       }
+
+       info_ptr = png_create_info_struct(png_ptr);
+       if (!info_ptr)
+       {
+               lprintf(__FILE__ ": png_create_info_struct() failed\n");
+               goto done;
+       }
+
+       // Start reading
+       png_init_io(png_ptr, fp);
+       png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_PACKING, NULL);
+       row_ptr = png_get_rows(png_ptr, info_ptr);
+       if (row_ptr == NULL)
+       {
+               lprintf(__FILE__ ": png_get_rows() failed\n");
+               goto done;
+       }
+
+       // lprintf("%s: %ix%i @ %ibpp\n", fname, (int)info_ptr->width, (int)info_ptr->height, info_ptr->pixel_depth);
+
+       switch (what)
+       {
+               case READPNG_BG:
+               {
+                       int height, width, h;
+                       unsigned short *dst = dest;
+                       if (info_ptr->pixel_depth != 24)
+                       {
+                               lprintf(__FILE__ ": bg image uses %ibpp, needed 24bpp\n", info_ptr->pixel_depth);
+                               break;
+                       }
+                       height = info_ptr->height;
+                       if (height > req_h)
+                               height = req_h;
+                       width = info_ptr->width;
+                       if (width > req_w)
+                               width = req_w;
+
+                       for (h = 0; h < height; h++)
+                       {
+                               unsigned char *src = row_ptr[h];
+                               int len = width;
+                               while (len--)
+                               {
+#ifdef PSP
+                                       *dst++ = ((src[2]&0xf8)<<8) | ((src[1]&0xf8)<<3) | (src[0] >> 3); // BGR
+#else
+                                       *dst++ = ((src[0]&0xf8)<<8) | ((src[1]&0xf8)<<3) | (src[2] >> 3); // RGB
+#endif
+                                       src += 3;
+                               }
+                               dst += req_w - width;
+                       }
+                       break;
+               }
+
+               case READPNG_FONT:
+               {
+                       int x, y, x1, y1;
+                       unsigned char *dst = dest;
+                       if (info_ptr->width != req_w || info_ptr->height != req_h)
+                       {
+                               lprintf(__FILE__ ": unexpected font image size %dx%d, needed %dx%d\n",
+                                       (int)info_ptr->width, (int)info_ptr->height, req_w, req_h);
+                               break;
+                       }
+                       if (info_ptr->pixel_depth != 8)
+                       {
+                               lprintf(__FILE__ ": font image uses %ibpp, needed 8bpp\n", info_ptr->pixel_depth);
+                               break;
+                       }
+                       for (y = 0; y < 16; y++)
+                       {
+                               for (x = 0; x < 16; x++)
+                               {
+                                       /* 16x16 grid of syms */
+                                       int sym_w = req_w / 16;
+                                       int sym_h = req_h / 16;
+                                       for (y1 = 0; y1 < sym_h; y1++)
+                                       {
+                                               unsigned char *src = row_ptr[y*sym_h + y1] + x*sym_w;
+                                               for (x1 = sym_w/2; x1 > 0; x1--, src+=2)
+                                                       *dst++ = ((src[0]^0xff) & 0xf0) | ((src[1]^0xff) >> 4);
+                                       }
+                               }
+                       }
+                       break;
+               }
+
+               case READPNG_SELECTOR:
+               {
+                       int x1, y1;
+                       unsigned char *dst = dest;
+                       if (info_ptr->width != req_w || info_ptr->height != req_h)
+                       {
+                               lprintf(__FILE__ ": unexpected selector image size %ix%i, needed %dx%d\n",
+                                       (int)info_ptr->width, (int)info_ptr->height, req_w, req_h);
+                               break;
+                       }
+                       if (info_ptr->pixel_depth != 8)
+                       {
+                               lprintf(__FILE__ ": selector image uses %ibpp, needed 8bpp\n", info_ptr->pixel_depth);
+                               break;
+                       }
+                       for (y1 = 0; y1 < req_h; y1++)
+                       {
+                               unsigned char *src = row_ptr[y1];
+                               for (x1 = req_w/2; x1 > 0; x1--, src+=2)
+                                       *dst++ = ((src[0]^0xff) & 0xf0) | ((src[1]^0xff) >> 4);
+                       }
+                       break;
+               }
+
+               case READPNG_24:
+               {
+                       int height, width, h;
+                       unsigned char *dst = dest;
+                       if (info_ptr->pixel_depth != 24)
+                       {
+                               lprintf(__FILE__ ": image uses %ibpp, needed 24bpp\n", info_ptr->pixel_depth);
+                               break;
+                       }
+                       height = info_ptr->height;
+                       if (height > req_h)
+                               height = req_h;
+                       width = info_ptr->width;
+                       if (width > req_w)
+                               width = req_w;
+
+                       for (h = 0; h < height; h++)
+                       {
+                               int len = width;
+                               unsigned char *src = row_ptr[h];
+                               dst += (req_w - width) * 3;
+                               for (len = width; len > 0; len--, dst+=3, src+=3)
+                                       dst[0] = src[2], dst[1] = src[1], dst[2] = src[0];
+                       }
+                       break;
+               }
+       }
+
+
+       ret = 0;
+done:
+       png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : NULL, (png_infopp)NULL);
+       fclose(fp);
+       return ret;
+}
+
+
diff --git a/frontend/common/readpng.h b/frontend/common/readpng.h
new file mode 100644 (file)
index 0000000..ce5d635
--- /dev/null
@@ -0,0 +1,18 @@
+typedef enum
+{
+       READPNG_BG = 1,
+       READPNG_FONT,
+       READPNG_SELECTOR,
+       READPNG_24,
+}
+readpng_what;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int readpng(void *dest, const char *fname, readpng_what what, int w, int h);
+
+#ifdef __cplusplus
+}
+#endif
index fffe979..b4889ea 100644 (file)
@@ -1,7 +1,10 @@
 /*
  * (C) Gražvydas "notaz" Ignotas, 2009-2010
  *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * This work is licensed under the terms of any of these licenses
+ * (at your option):
+ *  - GNU GPL, version 2 or later.
+ *  - GNU LGPL, version 2.1 or later.
  * See the COPYING file in the top-level directory.
  */
 
diff --git a/frontend/linux/in_evdev.c b/frontend/linux/in_evdev.c
new file mode 100644 (file)
index 0000000..0b73302
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * (C) Gražvydas "notaz" Ignotas, 2008-2010
+ *
+ * This work is licensed under the terms of any of these licenses
+ * (at your option):
+ *  - GNU GPL, version 2 or later.
+ *  - GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <errno.h>
+
+#include "../common/input.h"
+#include "in_evdev.h"
+
+typedef struct {
+       int fd;
+       int *kbits;
+       int abs_min_x;
+       int abs_max_x;
+       int abs_min_y;
+       int abs_max_y;
+       int abs_lzone;
+       int abs_lastx;
+       int abs_lasty;
+       int kc_first;
+       int kc_last;
+} in_evdev_t;
+
+#ifndef KEY_CNT
+#define KEY_CNT (KEY_MAX + 1)
+#endif
+
+#define KEYBITS_BIT(x) (keybits[(x)/sizeof(keybits[0])/8] & \
+       (1 << ((x) & (sizeof(keybits[0])*8-1))))
+
+#define KEYBITS_BIT_SET(x) (keybits[(x)/sizeof(keybits[0])/8] |= \
+       (1 << ((x) & (sizeof(keybits[0])*8-1))))
+
+#define KEYBITS_BIT_CLEAR(x) (keybits[(x)/sizeof(keybits[0])/8] &= \
+       ~(1 << ((x) & (sizeof(keybits[0])*8-1))))
+
+static const char * const in_evdev_prefix = "evdev:";
+static const char * const in_evdev_keys[KEY_CNT] = {
+       [0 ... KEY_MAX] = NULL,
+       [KEY_RESERVED] = "Reserved",            [KEY_ESC] = "Esc",
+       [KEY_1] = "1",                          [KEY_2] = "2",
+       [KEY_3] = "3",                          [KEY_4] = "4",
+       [KEY_5] = "5",                          [KEY_6] = "6",
+       [KEY_7] = "7",                          [KEY_8] = "8",
+       [KEY_9] = "9",                          [KEY_0] = "0",
+       [KEY_MINUS] = "Minus",                  [KEY_EQUAL] = "Equal",
+       [KEY_BACKSPACE] = "Backspace",          [KEY_TAB] = "Tab",
+       [KEY_Q] = "Q",                          [KEY_W] = "W",
+       [KEY_E] = "E",                          [KEY_R] = "R",
+       [KEY_T] = "T",                          [KEY_Y] = "Y",
+       [KEY_U] = "U",                          [KEY_I] = "I",
+       [KEY_O] = "O",                          [KEY_P] = "P",
+       [KEY_LEFTBRACE] = "LeftBrace",          [KEY_RIGHTBRACE] = "RightBrace",
+       [KEY_ENTER] = "Enter",                  [KEY_LEFTCTRL] = "LeftControl",
+       [KEY_A] = "A",                          [KEY_S] = "S",
+       [KEY_D] = "D",                          [KEY_F] = "F",
+       [KEY_G] = "G",                          [KEY_H] = "H",
+       [KEY_J] = "J",                          [KEY_K] = "K",
+       [KEY_L] = "L",                          [KEY_SEMICOLON] = "Semicolon",
+       [KEY_APOSTROPHE] = "Apostrophe",        [KEY_GRAVE] = "Grave",
+       [KEY_LEFTSHIFT] = "LeftShift",          [KEY_BACKSLASH] = "BackSlash",
+       [KEY_Z] = "Z",                          [KEY_X] = "X",
+       [KEY_C] = "C",                          [KEY_V] = "V",
+       [KEY_B] = "B",                          [KEY_N] = "N",
+       [KEY_M] = "M",                          [KEY_COMMA] = "Comma",
+       [KEY_DOT] = "Dot",                      [KEY_SLASH] = "Slash",
+       [KEY_RIGHTSHIFT] = "RightShift",        [KEY_KPASTERISK] = "KPAsterisk",
+       [KEY_LEFTALT] = "LeftAlt",              [KEY_SPACE] = "Space",
+       [KEY_CAPSLOCK] = "CapsLock",            [KEY_F1] = "F1",
+       [KEY_F2] = "F2",                        [KEY_F3] = "F3",
+       [KEY_F4] = "F4",                        [KEY_F5] = "F5",
+       [KEY_F6] = "F6",                        [KEY_F7] = "F7",
+       [KEY_F8] = "F8",                        [KEY_F9] = "F9",
+       [KEY_F10] = "F10",                      [KEY_NUMLOCK] = "NumLock",
+       [KEY_SCROLLLOCK] = "ScrollLock",        [KEY_KP7] = "KP7",
+       [KEY_KP8] = "KP8",                      [KEY_KP9] = "KP9",
+       [KEY_KPMINUS] = "KPMinus",              [KEY_KP4] = "KP4",
+       [KEY_KP5] = "KP5",                      [KEY_KP6] = "KP6",
+       [KEY_KPPLUS] = "KPPlus",                [KEY_KP1] = "KP1",
+       [KEY_KP2] = "KP2",                      [KEY_KP3] = "KP3",
+       [KEY_KP0] = "KP0",                      [KEY_KPDOT] = "KPDot",
+       [KEY_ZENKAKUHANKAKU] = "Zenkaku/Hankaku", [KEY_102ND] = "102nd",
+       [KEY_F11] = "F11",                      [KEY_F12] = "F12",
+       [KEY_KPJPCOMMA] = "KPJpComma",          [KEY_KPENTER] = "KPEnter",
+       [KEY_RIGHTCTRL] = "RightCtrl",          [KEY_KPSLASH] = "KPSlash",
+       [KEY_SYSRQ] = "SysRq",                  [KEY_RIGHTALT] = "RightAlt",
+       [KEY_LINEFEED] = "LineFeed",            [KEY_HOME] = "Home",
+       [KEY_UP] = "Up",                        [KEY_PAGEUP] = "PageUp",
+       [KEY_LEFT] = "Left",                    [KEY_RIGHT] = "Right",
+       [KEY_END] = "End",                      [KEY_DOWN] = "Down",
+       [KEY_PAGEDOWN] = "PageDown",            [KEY_INSERT] = "Insert",
+       [KEY_DELETE] = "Delete",                [KEY_MACRO] = "Macro",
+       [KEY_HELP] = "Help",                    [KEY_MENU] = "Menu",
+       [KEY_COFFEE] = "Coffee",                [KEY_DIRECTION] = "Direction",
+       [BTN_0] = "Btn0",                       [BTN_1] = "Btn1",
+       [BTN_2] = "Btn2",                       [BTN_3] = "Btn3",
+       [BTN_4] = "Btn4",                       [BTN_5] = "Btn5",
+       [BTN_6] = "Btn6",                       [BTN_7] = "Btn7",
+       [BTN_8] = "Btn8",                       [BTN_9] = "Btn9",
+       [BTN_LEFT] = "LeftBtn",                 [BTN_RIGHT] = "RightBtn",
+       [BTN_MIDDLE] = "MiddleBtn",             [BTN_SIDE] = "SideBtn",
+       [BTN_EXTRA] = "ExtraBtn",               [BTN_FORWARD] = "ForwardBtn",
+       [BTN_BACK] = "BackBtn",                 [BTN_TASK] = "TaskBtn",
+       [BTN_TRIGGER] = "Trigger",              [BTN_THUMB] = "ThumbBtn",
+       [BTN_THUMB2] = "ThumbBtn2",             [BTN_TOP] = "TopBtn",
+       [BTN_TOP2] = "TopBtn2",                 [BTN_PINKIE] = "PinkieBtn",
+       [BTN_BASE] = "BaseBtn",                 [BTN_BASE2] = "BaseBtn2",
+       [BTN_BASE3] = "BaseBtn3",               [BTN_BASE4] = "BaseBtn4",
+       [BTN_BASE5] = "BaseBtn5",               [BTN_BASE6] = "BaseBtn6",
+       [BTN_DEAD] = "BtnDead",                 [BTN_A] = "BtnA",
+       [BTN_B] = "BtnB",                       [BTN_C] = "BtnC",
+       [BTN_X] = "BtnX",                       [BTN_Y] = "BtnY",
+       [BTN_Z] = "BtnZ",                       [BTN_TL] = "BtnTL",
+       [BTN_TR] = "BtnTR",                     [BTN_TL2] = "BtnTL2",
+       [BTN_TR2] = "BtnTR2",                   [BTN_SELECT] = "BtnSelect",
+       [BTN_START] = "BtnStart",               [BTN_MODE] = "BtnMode",
+       [BTN_THUMBL] = "BtnThumbL",             [BTN_THUMBR] = "BtnThumbR",
+       [BTN_TOUCH] = "Touch",                  [BTN_STYLUS] = "Stylus",
+       [BTN_STYLUS2] = "Stylus2",              [BTN_TOOL_DOUBLETAP] = "Tool Doubletap",
+       [BTN_TOOL_TRIPLETAP] = "Tool Tripletap", [BTN_GEAR_DOWN] = "WheelBtn",
+       [BTN_GEAR_UP] = "Gear up",              [KEY_OK] = "Ok",
+};
+
+
+static void in_evdev_probe(void)
+{
+       long keybits[KEY_CNT / sizeof(long) / 8];
+       long absbits[(ABS_MAX+1) / sizeof(long) / 8];
+       int i;
+
+       // the kernel might support and return less keys then we know about,
+       // so make sure the buffers are clear.
+       memset(keybits, 0, sizeof(keybits));
+       memset(absbits, 0, sizeof(absbits));
+
+       for (i = 0;; i++)
+       {
+               int support = 0, count = 0;
+               int u, ret, fd, kc_first, kc_last;
+               in_evdev_t *dev;
+               char name[64];
+
+               snprintf(name, sizeof(name), "/dev/input/event%d", i);
+               fd = open(name, O_RDONLY|O_NONBLOCK);
+               if (fd == -1) {
+                       if (errno == EACCES)
+                               continue;       /* maybe we can access next one */
+                       break;
+               }
+
+               /* check supported events */
+               ret = ioctl(fd, EVIOCGBIT(0, sizeof(support)), &support);
+               if (ret == -1) {
+                       printf("in_evdev: ioctl failed on %s\n", name);
+                       goto skip;
+               }
+
+               if (!(support & (1 << EV_KEY)))
+                       goto skip;
+
+               ret = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits);
+               if (ret == -1) {
+                       printf("in_evdev: ioctl failed on %s\n", name);
+                       goto skip;
+               }
+
+               /* check for interesting keys */
+               kc_first = KEY_MAX;
+               kc_last = 0;
+               for (u = 0; u < KEY_CNT; u++) {
+                       if (KEYBITS_BIT(u)) {
+                               if (u < kc_first)
+                                       kc_first = u;
+                               if (u > kc_last)
+                                       kc_last = u;
+                               if (u != KEY_POWER && u != KEY_SLEEP && u != BTN_TOUCH)
+                                       count++;
+                       }
+               }
+
+               if (count == 0)
+                       goto skip;
+
+               dev = calloc(1, sizeof(*dev));
+               if (dev == NULL)
+                       goto skip;
+
+               ret = ioctl(fd, EVIOCGKEY(sizeof(keybits)), keybits);
+               if (ret == -1) {
+                       printf("Warning: EVIOCGKEY not supported, will have to track state\n");
+                       dev->kbits = calloc(KEY_CNT, sizeof(int));
+                       if (dev->kbits == NULL) {
+                               free(dev);
+                               goto skip;
+                       }
+               }
+
+               /* check for abs too */
+               if (support & (1 << EV_ABS)) {
+                       struct input_absinfo ainfo;
+                       int dist;
+                       ret = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits);
+                       if (ret == -1)
+                               goto no_abs;
+                       if (absbits[0] & (1 << ABS_X)) {
+                               ret = ioctl(fd, EVIOCGABS(ABS_X), &ainfo);
+                               if (ret == -1)
+                                       goto no_abs;
+                               dist = ainfo.maximum - ainfo.minimum;
+                               dev->abs_lzone = dist / 4;
+                               dev->abs_min_x = ainfo.minimum;
+                               dev->abs_max_x = ainfo.maximum;
+                       }
+                       if (absbits[0] & (1 << ABS_Y)) {
+                               ret = ioctl(fd, EVIOCGABS(ABS_Y), &ainfo);
+                               if (ret == -1)
+                                       goto no_abs;
+                               dist = ainfo.maximum - ainfo.minimum;
+                               dev->abs_min_y = ainfo.minimum;
+                               dev->abs_max_y = ainfo.maximum;
+                       }
+               }
+
+no_abs:
+               dev->fd = fd;
+               dev->kc_first = kc_first;
+               dev->kc_last = kc_last;
+               strcpy(name, in_evdev_prefix);
+               ioctl(fd, EVIOCGNAME(sizeof(name)-6), name+6);
+               printf("in_evdev: found \"%s\" with %d events (type %08x)\n",
+                       name+6, count, support);
+               in_register(name, IN_DRVID_EVDEV, fd, dev, KEY_CNT, in_evdev_keys, 0);
+               continue;
+
+skip:
+               close(fd);
+       }
+}
+
+static void in_evdev_free(void *drv_data)
+{
+       in_evdev_t *dev = drv_data;
+       if (dev == NULL)
+               return;
+       close(dev->fd);
+       free(dev);
+}
+
+static int in_evdev_get_bind_count(void)
+{
+       return KEY_CNT;
+}
+
+static void or_binds(const int *binds, int key, int *result)
+{
+       int t;
+       for (t = 0; t < IN_BINDTYPE_COUNT; t++)
+               result[t] |= binds[IN_BIND_OFFS(key, t)];
+}
+
+/* ORs result with binds of pressed buttons
+ * XXX: should measure performance hit of this func, might need to optimize */
+int in_evdev_update(void *drv_data, const int *binds, int *result)
+{
+       struct input_event ev[16];
+       struct input_absinfo ainfo;
+       int keybits_[KEY_CNT / sizeof(int)];
+       int *keybits = keybits_;
+       in_evdev_t *dev = drv_data;
+       int rd, ret, u, lzone;
+
+       if (dev->kbits == NULL) {
+               ret = ioctl(dev->fd, EVIOCGKEY(sizeof(keybits_)), keybits_);
+               if (ret == -1) {
+                       perror("in_evdev: ioctl failed");
+                       return -1;
+               }
+       }
+       else {
+               keybits = dev->kbits;
+               while (1) {
+                       rd = read(dev->fd, ev, sizeof(ev));
+                       if (rd < (int)sizeof(ev[0])) {
+                               if (errno != EAGAIN)
+                                       perror("in_evdev: read failed");
+                               break;
+                       }
+                       for (u = 0; u < rd / sizeof(ev[0]); u++) {
+                               if (ev[u].type != EV_KEY)
+                                       continue;
+                               else if (ev[u].value == 1)
+                                       KEYBITS_BIT_SET(ev[u].code);
+                               else if (ev[u].value == 0)
+                                       KEYBITS_BIT_CLEAR(ev[u].code);
+                       }
+               }
+       }
+
+       for (u = dev->kc_first; u <= dev->kc_last; u++) {
+               if (KEYBITS_BIT(u))
+                       or_binds(binds, u, result);
+       }
+
+       /* map X and Y absolute to UDLR */
+       lzone = dev->abs_lzone;
+       if (lzone != 0) {
+               ret = ioctl(dev->fd, EVIOCGABS(ABS_X), &ainfo);
+               if (ret != -1) {
+                       if (ainfo.value < dev->abs_min_x + lzone) or_binds(binds, KEY_LEFT, result);
+                       if (ainfo.value > dev->abs_max_x - lzone) or_binds(binds, KEY_RIGHT, result);
+               }
+       }
+       if (lzone != 0) {
+               ret = ioctl(dev->fd, EVIOCGABS(ABS_Y), &ainfo);
+               if (ret != -1) {
+                       if (ainfo.value < dev->abs_min_y + lzone) or_binds(binds, KEY_UP, result);
+                       if (ainfo.value > dev->abs_max_y - lzone) or_binds(binds, KEY_DOWN, result);
+               }
+       }
+
+       return 0;
+}
+
+static int in_evdev_set_blocking(in_evdev_t *dev, int y)
+{
+       long flags;
+       int ret;
+
+       flags = (long)fcntl(dev->fd, F_GETFL);
+       if ((int)flags == -1) {
+               perror("in_evdev: F_GETFL fcntl failed");
+               return -1;
+       }
+
+       if (flags & O_NONBLOCK) {
+               /* flush the event queue */
+               struct input_event ev;
+               do {
+                       ret = read(dev->fd, &ev, sizeof(ev));
+               }
+               while (ret == sizeof(ev));
+       }
+
+       if (y)
+               flags &= ~O_NONBLOCK;
+       else
+               flags |=  O_NONBLOCK;
+       ret = fcntl(dev->fd, F_SETFL, flags);
+       if (ret == -1) {
+               perror("in_evdev: F_SETFL fcntl failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int in_evdev_set_config(void *drv_data, int what, int val)
+{
+       in_evdev_t *dev = drv_data;
+       int tmp;
+
+       switch (what) {
+       case IN_CFG_BLOCKING:
+               return in_evdev_set_blocking(dev, val);
+       case IN_CFG_ABS_DEAD_ZONE:
+               if (val < 1 || val > 99 || dev->abs_lzone == 0)
+                       return -1;
+               /* XXX: based on X axis only, for now.. */
+               tmp = (dev->abs_max_x - dev->abs_min_x) / 2;
+               dev->abs_lzone = tmp - tmp * val / 100;
+               if (dev->abs_lzone < 1)
+                       dev->abs_lzone = 1;
+               else if (dev->abs_lzone >= tmp)
+                       dev->abs_lzone = tmp - 1;
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int in_evdev_update_keycode(void *data, int *is_down)
+{
+       int ret_kc = -1, ret_down = 0;
+       in_evdev_t *dev = data;
+       struct input_event ev;
+       int rd;
+
+       while (1)
+       {
+               rd = read(dev->fd, &ev, sizeof(ev));
+               if (rd < (int) sizeof(ev)) {
+                       if (errno != EAGAIN) {
+                               perror("in_evdev: error reading");
+                               sleep(1);
+                       }
+                       goto out;
+               }
+
+               if (ev.type == EV_KEY) {
+                       if (ev.value < 0 || ev.value > 1)
+                               continue;
+                       ret_kc = ev.code;
+                       ret_down = ev.value;
+                       goto out;
+               }
+               else if (ev.type == EV_ABS)
+               {
+                       int lzone = dev->abs_lzone, down = 0, *last;
+
+                       // map absolute to up/down/left/right
+                       if (lzone != 0 && ev.code == ABS_X) {
+                               if (ev.value < dev->abs_min_x + lzone)
+                                       down = KEY_LEFT;
+                               else if (ev.value > dev->abs_max_x - lzone)
+                                       down = KEY_RIGHT;
+                               last = &dev->abs_lastx;
+                       }
+                       else if (lzone != 0 && ev.code == ABS_Y) {
+                               if (ev.value < dev->abs_min_y + lzone)
+                                       down = KEY_UP;
+                               else if (ev.value > dev->abs_max_y - lzone)
+                                       down = KEY_DOWN;
+                               last = &dev->abs_lasty;
+                       }
+                       else
+                               continue;
+
+                       if (down == *last)
+                               continue;
+
+                       if (down == 0 || *last != 0) {
+                               /* key up or direction change, return up event for old key */
+                               ret_kc = *last;
+                               ret_down = 0;
+                               *last = 0;
+                               goto out;
+                       }
+                       ret_kc = *last = down;
+                       ret_down = 1;
+                       goto out;
+               }
+       }
+
+out:
+       if (is_down != NULL)
+               *is_down = ret_down;
+
+       return ret_kc;
+}
+
+static const struct {
+       short key;
+       short pbtn;
+} key_pbtn_map[] =
+{
+       { KEY_UP,       PBTN_UP },
+       { KEY_DOWN,     PBTN_DOWN },
+       { KEY_LEFT,     PBTN_LEFT },
+       { KEY_RIGHT,    PBTN_RIGHT },
+       /* XXX: maybe better set this from it's plat code somehow */
+       /* Pandora */
+       { KEY_END,      PBTN_MOK },
+       { KEY_PAGEDOWN, PBTN_MBACK },
+       { KEY_HOME,     PBTN_MA2 },
+       { KEY_PAGEUP,   PBTN_MA3 },
+       { KEY_LEFTCTRL,   PBTN_MENU },
+       { KEY_RIGHTSHIFT, PBTN_L },
+       { KEY_RIGHTCTRL,  PBTN_R },
+       /* Caanoo */
+       { BTN_THUMB2,   PBTN_MOK },
+       { BTN_THUMB,    PBTN_MBACK },
+       { BTN_TRIGGER,  PBTN_MA2 },
+       { BTN_TOP,      PBTN_MA3 },
+       { BTN_BASE,     PBTN_MENU },
+       { BTN_TOP2,     PBTN_L },
+       { BTN_PINKIE,   PBTN_R },
+       /* "normal" keyboards */
+       { KEY_ENTER,    PBTN_MOK },
+       { KEY_ESC,      PBTN_MBACK },
+       { KEY_A,        PBTN_MA2 },
+       { KEY_S,        PBTN_MA3 },
+       { KEY_BACKSLASH,  PBTN_MENU },
+       { KEY_LEFTBRACE,  PBTN_L },
+       { KEY_RIGHTBRACE, PBTN_R },
+};
+
+#define KEY_PBTN_MAP_SIZE (sizeof(key_pbtn_map) / sizeof(key_pbtn_map[0]))
+
+static int in_evdev_menu_translate(void *drv_data, int keycode)
+{
+       in_evdev_t *dev = drv_data;
+       int i;
+
+       if (keycode < 0)
+       {
+               /* menu -> kc */
+               keycode = -keycode;
+               for (i = 0; i < KEY_PBTN_MAP_SIZE; i++)
+                       if (key_pbtn_map[i].pbtn == keycode) {
+                               int k = key_pbtn_map[i].key;
+                               /* should really check EVIOCGBIT, but this is enough for now */
+                               if (dev->kc_first <= k && k <= dev->kc_last)
+                                       return k;
+                       }
+       }
+       else
+       {
+               for (i = 0; i < KEY_PBTN_MAP_SIZE; i++)
+                       if (key_pbtn_map[i].key == keycode)
+                               return key_pbtn_map[i].pbtn;
+       }
+
+       return 0;
+}
+
+/* FIXME: move to plat */
+#if 0
+static const struct {
+       short code;
+       char btype;
+       char bit;
+} in_evdev_def_binds[] =
+{
+       /* MXYZ SACB RLDU */
+       { KEY_UP,       IN_BINDTYPE_PLAYER12, 0 },
+       { KEY_DOWN,     IN_BINDTYPE_PLAYER12, 1 },
+       { KEY_LEFT,     IN_BINDTYPE_PLAYER12, 2 },
+       { KEY_RIGHT,    IN_BINDTYPE_PLAYER12, 3 },
+       { KEY_S,        IN_BINDTYPE_PLAYER12, 4 },      /* B */
+       { KEY_D,        IN_BINDTYPE_PLAYER12, 5 },      /* C */
+       { KEY_A,        IN_BINDTYPE_PLAYER12, 6 },      /* A */
+       { KEY_ENTER,    IN_BINDTYPE_PLAYER12, 7 },
+       { KEY_BACKSLASH, IN_BINDTYPE_EMU, PEVB_MENU },
+       /* Pandora */
+       { KEY_PAGEDOWN, IN_BINDTYPE_PLAYER12, 4 },
+       { KEY_END,      IN_BINDTYPE_PLAYER12, 5 },
+       { KEY_HOME,     IN_BINDTYPE_PLAYER12, 6 },
+       { KEY_LEFTALT,  IN_BINDTYPE_PLAYER12, 7 },
+       { KEY_RIGHTSHIFT,IN_BINDTYPE_EMU, PEVB_STATE_SAVE },
+       { KEY_RIGHTCTRL, IN_BINDTYPE_EMU, PEVB_STATE_LOAD },
+       { KEY_LEFTCTRL,  IN_BINDTYPE_EMU, PEVB_MENU },
+       /* Caanoo */
+       { BTN_THUMB,    IN_BINDTYPE_PLAYER12, 4 },      /* B */
+       { BTN_THUMB2,   IN_BINDTYPE_PLAYER12, 5 },      /* C */
+       { BTN_TRIGGER,  IN_BINDTYPE_PLAYER12, 6 },      /* A */
+       { BTN_BASE3,    IN_BINDTYPE_PLAYER12, 7 },
+       { BTN_TOP2,     IN_BINDTYPE_EMU, PEVB_STATE_SAVE },
+       { BTN_PINKIE,   IN_BINDTYPE_EMU, PEVB_STATE_LOAD },
+       { BTN_BASE,     IN_BINDTYPE_EMU, PEVB_MENU },
+};
+#endif
+
+static void in_evdev_get_def_binds(int *binds)
+{
+       int i;
+
+       for (i = 0; in_evdev_defbinds[i].bit != 0; i++) {
+               binds[IN_BIND_OFFS(in_evdev_defbinds[i].code, in_evdev_defbinds[i].btype)] =
+                       1 << in_evdev_defbinds[i].bit;
+       }
+}
+
+/* remove binds of missing keys, count remaining ones */
+static int in_evdev_clean_binds(void *drv_data, int *binds, int *def_binds)
+{
+       int keybits[KEY_CNT / sizeof(int)];
+       in_evdev_t *dev = drv_data;
+       int i, t, ret, offs, count = 0;
+
+       ret = ioctl(dev->fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits);
+       if (ret == -1) {
+               perror("in_evdev: ioctl failed");
+               memset(keybits, 0xff, sizeof(keybits)); /* mark all as good */
+       }
+
+       if (dev->abs_lzone != 0) {
+               KEYBITS_BIT_SET(KEY_LEFT);
+               KEYBITS_BIT_SET(KEY_RIGHT);
+       }
+       if (dev->abs_lzone != 0) {
+               KEYBITS_BIT_SET(KEY_UP);
+               KEYBITS_BIT_SET(KEY_DOWN);
+       }
+
+       for (i = 0; i < KEY_CNT; i++) {
+               for (t = 0; t < IN_BINDTYPE_COUNT; t++) {
+                       offs = IN_BIND_OFFS(i, t);
+                       if (!KEYBITS_BIT(i))
+                               binds[offs] = def_binds[offs] = 0;
+                       if (binds[offs])
+                               count++;
+               }
+       }
+
+       return count;
+}
+
+void in_evdev_init(void *vdrv)
+{
+       in_drv_t *drv = vdrv;
+
+       drv->prefix = in_evdev_prefix;
+       drv->probe = in_evdev_probe;
+       drv->free = in_evdev_free;
+       drv->get_bind_count = in_evdev_get_bind_count;
+       drv->get_def_binds = in_evdev_get_def_binds;
+       drv->clean_binds = in_evdev_clean_binds;
+       drv->set_config = in_evdev_set_config;
+       drv->update_keycode = in_evdev_update_keycode;
+       drv->menu_translate = in_evdev_menu_translate;
+}
+
diff --git a/frontend/linux/in_evdev.h b/frontend/linux/in_evdev.h
new file mode 100644 (file)
index 0000000..49682c3
--- /dev/null
@@ -0,0 +1,6 @@
+
+void in_evdev_init(void *vdrv);
+int  in_evdev_update(void *drv_data, const int *binds, int *result);
+
+/* to be set somewhere in platform code */
+extern struct in_default_bind in_evdev_defbinds[];
diff --git a/frontend/linux/oshide.c b/frontend/linux/oshide.c
new file mode 100644 (file)
index 0000000..203691f
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * (C) Gražvydas "notaz" Ignotas, 2009-2010
+ *
+ * This work is licensed under the terms of any of these licenses
+ * (at your option):
+ *  - GNU GPL, version 2 or later.
+ *  - GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+
+#include <dlfcn.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <linux/kd.h>
+
+#define PFX "oshide: "
+
+#define FPTR(f) typeof(f) * p##f
+#define FPTR_LINK(xf, dl, f) { \
+       xf.p##f = dlsym(dl, #f); \
+       if (xf.p##f == NULL) { \
+               fprintf(stderr, "missing symbol: %s\n", #f); \
+               goto fail; \
+       } \
+}
+
+struct xfuncs {
+FPTR(XCreateBitmapFromData);
+FPTR(XCreatePixmapCursor);
+FPTR(XFreePixmap);
+FPTR(XOpenDisplay);
+FPTR(XDisplayName);
+FPTR(XCloseDisplay);
+FPTR(XCreateSimpleWindow);
+FPTR(XChangeWindowAttributes);
+FPTR(XSelectInput);
+FPTR(XMapWindow);
+FPTR(XNextEvent);
+FPTR(XCheckTypedEvent);
+FPTR(XUnmapWindow);
+FPTR(XGrabKeyboard);
+};
+
+
+static Cursor transparent_cursor(struct xfuncs *xf, Display *display, Window win)
+{
+       Cursor cursor;
+       Pixmap pix;
+       XColor dummy;
+       char d = 0;
+
+       memset(&dummy, 0, sizeof(dummy));
+       pix = xf->pXCreateBitmapFromData(display, win, &d, 1, 1);
+       cursor = xf->pXCreatePixmapCursor(display, pix, pix,
+                       &dummy, &dummy, 0, 0);
+       xf->pXFreePixmap(display, pix);
+       return cursor;
+}
+
+static void *x11h_handler(void *arg)
+{
+       struct xfuncs xf;
+       unsigned int display_width, display_height;
+       XSetWindowAttributes attributes;
+       Window win;
+       XEvent report;
+       Display *display;
+       Visual *visual;
+       void *x11lib;
+       int screen;
+
+       memset(&xf, 0, sizeof(xf));
+       x11lib = dlopen("libX11.so.6", RTLD_LAZY);
+       if (x11lib == NULL) {
+               fprintf(stderr, "libX11.so load failed:\n%s\n", dlerror());
+               goto fail;
+       }
+       FPTR_LINK(xf, x11lib, XCreateBitmapFromData);
+       FPTR_LINK(xf, x11lib, XCreatePixmapCursor);
+       FPTR_LINK(xf, x11lib, XFreePixmap);
+       FPTR_LINK(xf, x11lib, XOpenDisplay);
+       FPTR_LINK(xf, x11lib, XDisplayName);
+       FPTR_LINK(xf, x11lib, XCloseDisplay);
+       FPTR_LINK(xf, x11lib, XCreateSimpleWindow);
+       FPTR_LINK(xf, x11lib, XChangeWindowAttributes);
+       FPTR_LINK(xf, x11lib, XSelectInput);
+       FPTR_LINK(xf, x11lib, XMapWindow);
+       FPTR_LINK(xf, x11lib, XNextEvent);
+       FPTR_LINK(xf, x11lib, XCheckTypedEvent);
+       FPTR_LINK(xf, x11lib, XUnmapWindow);
+       FPTR_LINK(xf, x11lib, XGrabKeyboard);
+
+       //XInitThreads();
+
+       display = xf.pXOpenDisplay(NULL);
+       if (display == NULL)
+       {
+               fprintf(stderr, "cannot connect to X server %s, X handling disabled.\n",
+                               xf.pXDisplayName(NULL));
+               goto fail2;
+       }
+
+       visual = DefaultVisual(display, 0);
+       if (visual->class != TrueColor)
+               fprintf(stderr, PFX "warning: non true color visual\n");
+
+       printf(PFX "X vendor: %s, rel: %d, display: %s, protocol ver: %d.%d\n", ServerVendor(display),
+               VendorRelease(display), DisplayString(display), ProtocolVersion(display),
+               ProtocolRevision(display));
+
+       screen = DefaultScreen(display);
+
+       display_width = DisplayWidth(display, screen);
+       display_height = DisplayHeight(display, screen);
+       printf(PFX "display is %dx%d\n", display_width, display_height);
+
+       win = xf.pXCreateSimpleWindow(display,
+                       RootWindow(display, screen),
+                       0, 0, display_width, display_height, 0,
+                       BlackPixel(display, screen),
+                       BlackPixel(display, screen));
+
+       attributes.override_redirect = True;
+       attributes.cursor = transparent_cursor(&xf, display, win);
+       xf.pXChangeWindowAttributes(display, win, CWOverrideRedirect | CWCursor, &attributes);
+
+       xf.pXSelectInput(display, win, ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask);
+       xf.pXMapWindow(display, win);
+       xf.pXGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
+       // XSetIOErrorHandler
+
+       while (1)
+       {
+               xf.pXNextEvent(display, &report);
+               switch (report.type)
+               {
+                       case Expose:
+                               while (xf.pXCheckTypedEvent(display, Expose, &report))
+                                       ;
+                               break;
+
+                       case FocusOut:
+                               // XFocusChangeEvent
+                               // printf("focus out\n");
+                               // xf.pXUnmapWindow(display, win);
+                               break;
+
+                       case KeyPress:
+                               // printf("press %d\n", report.xkey.keycode);
+                               break;
+
+                       default:
+                               break;
+               }
+       }
+
+fail2:
+       dlclose(x11lib);
+fail:
+       fprintf(stderr, "x11 handling disabled.\n");
+       return NULL;
+}
+
+static struct termios g_kbd_termios_saved;
+static int g_kbdfd;
+
+static void hidecon_start(void)
+{
+       struct termios kbd_termios;
+       int mode;
+
+       g_kbdfd = open("/dev/tty", O_RDWR);
+       if (g_kbdfd == -1) {
+               perror(PFX "open /dev/tty");
+               return;
+       }
+
+       if (ioctl(g_kbdfd, KDGETMODE, &mode) == -1) {
+               perror(PFX "(not hiding FB): KDGETMODE");
+               goto fail;
+       }
+
+       if (tcgetattr(g_kbdfd, &kbd_termios) == -1) {
+               perror(PFX "tcgetattr");
+               goto fail;
+       }
+
+       g_kbd_termios_saved = kbd_termios;
+       kbd_termios.c_lflag &= ~(ICANON | ECHO); // | ISIG);
+       kbd_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
+       kbd_termios.c_cc[VMIN] = 0;
+       kbd_termios.c_cc[VTIME] = 0;
+
+       if (tcsetattr(g_kbdfd, TCSAFLUSH, &kbd_termios) == -1) {
+               perror(PFX "tcsetattr");
+               goto fail;
+       }
+
+       if (ioctl(g_kbdfd, KDSETMODE, KD_GRAPHICS) == -1) {
+               perror(PFX "KDSETMODE KD_GRAPHICS");
+               tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved);
+               goto fail;
+       }
+
+       return;
+
+fail:
+       close(g_kbdfd);
+       g_kbdfd = -1;
+}
+
+static void hidecon_end(void)
+{
+       if (g_kbdfd < 0)
+               return;
+
+       if (ioctl(g_kbdfd, KDSETMODE, KD_TEXT) == -1)
+               perror(PFX "KDSETMODE KD_TEXT");
+
+       if (tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved) == -1)
+               perror(PFX "tcsetattr");
+
+       close(g_kbdfd);
+       g_kbdfd = -1;
+}
+
+int oshide_init(void)
+{
+       pthread_t tid;
+       int ret;
+
+       ret = pthread_create(&tid, NULL, x11h_handler, NULL);
+       if (ret != 0) {
+               fprintf(stderr, PFX "failed to create thread: %d\n", ret);
+               return ret;
+       }
+       pthread_detach(tid);
+
+       hidecon_start();
+
+       return 0;
+}
+
+void oshide_finish(void)
+{
+       /* XXX: the X thread.. */
+
+       hidecon_end();
+}
+
+#if 0
+int main()
+{
+       x11h_init();
+       sleep(5);
+}
+#endif
diff --git a/frontend/linux/oshide.h b/frontend/linux/oshide.h
new file mode 100644 (file)
index 0000000..f661305
--- /dev/null
@@ -0,0 +1,3 @@
+int  oshide_init(void);
+void oshide_finish(void);
+
diff --git a/frontend/linux/plat.c b/frontend/linux/plat.c
new file mode 100644 (file)
index 0000000..1e107f4
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * (C) Gražvydas "notaz" Ignotas, 2008-2010
+ *
+ * This work is licensed under the terms of any of these licenses
+ * (at your option):
+ *  - GNU GPL, version 2 or later.
+ *  - GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "../common/plat.h"
+
+
+int plat_is_dir(const char *path)
+{
+       DIR *dir;
+       if ((dir = opendir(path))) {
+               closedir(dir);
+               return 1;
+       }
+       return 0;
+}
+
+int plat_get_root_dir(char *dst, int len)
+{
+       int j, ret;
+
+       ret = readlink("/proc/self/exe", dst, len - 1);
+       if (ret < 0) {
+               perror("readlink");
+               ret = 0;
+       }
+       dst[ret] = 0;
+
+       for (j = strlen(dst); j > 0; j--)
+               if (dst[j] == '/') {
+                       dst[++j] = 0;
+                       break;
+               }
+
+       return 0;
+}
+
+#ifdef __GP2X__
+/* Wiz has a borked gettimeofday().. */
+#define plat_get_ticks_ms plat_get_ticks_ms_good
+#define plat_get_ticks_us plat_get_ticks_us_good
+#endif
+
+unsigned int plat_get_ticks_ms(void)
+{
+       struct timeval tv;
+       unsigned int ret;
+
+       gettimeofday(&tv, NULL);
+
+       ret = (unsigned)tv.tv_sec * 1000;
+       /* approximate /= 1000 */
+       ret += ((unsigned)tv.tv_usec * 4195) >> 22;
+
+       return ret;
+}
+
+unsigned int plat_get_ticks_us(void)
+{
+       struct timeval tv;
+       unsigned int ret;
+
+       gettimeofday(&tv, NULL);
+
+       ret = (unsigned)tv.tv_sec * 1000000;
+       ret += (unsigned)tv.tv_usec;
+
+       return ret;
+}
+
+void plat_sleep_ms(int ms)
+{
+       usleep(ms * 1000);
+}
+
+int plat_wait_event(int *fds_hnds, int count, int timeout_ms)
+{
+       struct timeval tv, *timeout = NULL;
+       int i, ret, fdmax = -1;
+       fd_set fdset;
+
+       if (timeout_ms >= 0) {
+               tv.tv_sec = timeout_ms / 1000;
+               tv.tv_usec = (timeout_ms % 1000) * 1000;
+               timeout = &tv;
+       }
+
+       FD_ZERO(&fdset);
+       for (i = 0; i < count; i++) {
+               if (fds_hnds[i] > fdmax) fdmax = fds_hnds[i];
+               FD_SET(fds_hnds[i], &fdset);
+       }
+
+       ret = select(fdmax + 1, &fdset, NULL, NULL, timeout);
+       if (ret == -1)
+       {
+               perror("plat_wait_event: select failed");
+               sleep(1);
+               return -1;
+       }
+
+       if (ret == 0)
+               return -1; /* timeout */
+
+       ret = -1;
+       for (i = 0; i < count; i++)
+               if (FD_ISSET(fds_hnds[i], &fdset))
+                       ret = fds_hnds[i];
+
+       return ret;
+}
+
+void *plat_mmap(unsigned long addr, size_t size)
+{
+       void *req, *ret;
+
+       req = (void *)addr;
+       ret = mmap(req, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+       if (ret == MAP_FAILED)
+               return NULL;
+       if (ret != req)
+               printf("warning: mmaped to %p, requested %p\n", ret, req);
+
+       return ret;
+}
+
+void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
+{
+       void *ret;
+
+       ret = mremap(ptr, oldsize, newsize, MREMAP_MAYMOVE);
+       if (ret == MAP_FAILED)
+               return NULL;
+       if (ret != ptr)
+               printf("warning: mremap moved: %p -> %p\n", ptr, ret);
+
+       return ret;
+}
+
+void plat_munmap(void *ptr, size_t size)
+{
+       munmap(ptr, size);
+}
+
+/* lprintf */
+void lprintf(const char *fmt, ...)
+{
+       va_list vl;
+
+       va_start(vl, fmt);
+       vprintf(fmt, vl);
+       va_end(vl);
+}
+