Adding outdated source code into master

This commit is contained in:
IlushaShurupov 2023-08-20 11:27:47 +03:00 committed by Ilya Shurupov
parent be206950f0
commit 74aaf9a0a1
27 changed files with 6854 additions and 0 deletions

View file

@ -0,0 +1,943 @@
/*
EasyTab.h - Single-header multi-platform tablet library
https://github.com/ApoorvaJ/EasyTab
----------------------------------------------------------------------------
USAGE
----------------------------------------------------------------------------
1) Add the following lines in exactly one of your cpp files to compile the
implementation.
#define EASYTAB_IMPLEMENTATION
#include "easytab.h"
2) Call EasyTab_Load() with correct parameters to initialize EasyTab. These
parameters vary per OS, so look at the function declarations or examples
below. Function returns EASYTAB_OK if initialization was successful.
3) Call EasyTab_HandleEvent() in your message-handling code. The function
returns EASYTAB_OK if the message was a tablet message, and
EASYTAB_EVENT_NOT_HANDLED otherwise.
4) Call EasyTab_Unload() in your shutdown code.
5) Once initialized, you can query tablet state using the EasyTab pointer.
e.g.:
EasyTab->PosX // X position of the pen
EasyTab->PosY // Y position of the pen
EasyTab->Pressure // Pressure of the pen ranging from 0.0f to 1.0f
For more info, have a look at the EasyTabInfo struct below.
* Add -lXi to compiler options to link XInput on Linux.
----------------------------------------------------------------------------
EXAMPLES
----------------------------------------------------------------------------
1) Windows:
int CALLBACK WinMain(...)
{
HWND Window;
...
if (EasyTab_Load(Window) != EASYTAB_OK) // Load
{
OutputDebugStringA("Tablet init failed\n");
}
...
// Once you've set up EasyTab loading, unloading and event handling,
// use the EasyTab variable at any point in your program to access
// the tablet state:
// EasyTab->PosX
// EasyTab->PosY
// EasyTab->Pressure
// For more tablet information, look at the EasyTabInfo struct.
...
EasyTab_Unload(); // Unload
}
LRESULT CALLBACK WindowProc(
HWND Window,
UINT Message,
WPARAM WParam,
LPARAM LParam)
{
if (EasyTab_HandleEvent(Window, Message, LParam, WParam) == EASYTAB_OK) // Event
{
return true; // Tablet event handled
}
switch (Message)
{
...
}
}
2) Linux:
int main(...)
{
Display* Disp;
Window Win;
...
if (EasyTab_Load(Disp, Win) != EASYTAB_OK) // Load
{
printf("Tablet init failed\n");
}
...
while (XPending(Disp)) // Event loop
{
XEvent Event;
XNextEvent(XlibDisplay, &Event);
if (EasyTab_HandleEvent(&Event) == EASYTAB_OK) // Event
{
continue; // Tablet event handled
}
switch (Event.type)
{
...
}
}
...
// Once you've set up EasyTab loading, unloading and event handling,
// use the EasyTab variable at any point in your program to access
// the tablet state:
// EasyTab->PosX
// EasyTab->PosY
// EasyTab->Pressure
// For more tablet information, look at the EasyTabInfo struct.
...
EasyTab_Unload(); // Unload
}
----------------------------------------------------------------------------
CREDITS
----------------------------------------------------------------------------
Apoorva Joshi apoorvaj.io
Sergio Gonzalez s3rg.io
This library is coded in the spirit of the stb libraries and follows the stb
guidelines.
*/
// TODO: Null checks and warnings for EasyTab
// TODO: Differentiate between stylus and eraser in the API
// TODO: Linux support for relative mode
// TODO: Documentation for relative mode
// =============================================================================
// EasyTab header section
// =============================================================================
#ifndef EASYTAB_H
#define EASYTAB_H
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef __linux__
#include <X11/extensions/XInput.h>
#endif // __linux__
#ifdef _WIN32
#include <windows.h>
#endif // _WIN32
typedef enum
{
EASYTAB_OK = 0,
// Errors
EASYTAB_MEMORY_ERROR = -1,
EASYTAB_X11_ERROR = -2,
EASYTAB_DLL_LOAD_ERROR = -3,
EASYTAB_WACOM_WIN32_ERROR = -4,
EASYTAB_INVALID_FUNCTION_ERROR = -5,
EASYTAB_EVENT_NOT_HANDLED = -16,
} EasyTabResult;
typedef enum
{
EASYTAB_TRACKING_MODE_SYSTEM = 0,
EASYTAB_TRACKING_MODE_RELATIVE = 1,
} EasyTabTrackingMode;
#ifdef WIN32
// -----------------------------------------------------------------------------
// wintab.h
// -----------------------------------------------------------------------------
#if 1
DECLARE_HANDLE(HMGR);
DECLARE_HANDLE(HCTX);
typedef DWORD WTPKT;
typedef DWORD FIX32;
// Messages
#if 1
#define WT_DEFBASE 0x7FF0
#define WT_MAXOFFSET 0xF
#define _WT_PACKET(b) ((b)+0)
#define _WT_CTXOPEN(b) ((b)+1)
#define _WT_CTXCLOSE(b) ((b)+2)
#define _WT_CTXUPDATE(b) ((b)+3)
#define _WT_CTXOVERLAP(b) ((b)+4)
#define _WT_PROXIMITY(b) ((b)+5)
#define _WT_INFOCHANGE(b) ((b)+6)
#define _WT_CSRCHANGE(b) ((b)+7) /* 1.1 */
#define _WT_PACKETEXT(b) ((b)+8) /* 1.4 */
#define _WT_MAX(b) ((b)+WT_MAXOFFSET)
#define WT_PACKET _WT_PACKET(WT_DEFBASE)
#define WT_CTXOPEN _WT_CTXOPEN(WT_DEFBASE)
#define WT_CTXCLOSE _WT_CTXCLOSE(WT_DEFBASE)
#define WT_CTXUPDATE _WT_CTXUPDATE(WT_DEFBASE)
#define WT_CTXOVERLAP _WT_CTXOVERLAP(WT_DEFBASE)
#define WT_PROXIMITY _WT_PROXIMITY(WT_DEFBASE)
#define WT_INFOCHANGE _WT_INFOCHANGE(WT_DEFBASE)
#define WT_CSRCHANGE _WT_CSRCHANGE(WT_DEFBASE) /* 1.1 */
#define WT_PACKETEXT _WT_PACKETEXT(WT_DEFBASE) /* 1.4 */
#define WT_MAX _WT_MAX(WT_DEFBASE)
#endif // Messages
// Flags
#if 1
#define CTX_NAME 1
#define CTX_OPTIONS 2
#define CTX_STATUS 3
#define CTX_LOCKS 4
#define CTX_MSGBASE 5
#define CTX_DEVICE 6
#define CTX_PKTRATE 7
#define CTX_PKTDATA 8
#define CTX_PKTMODE 9
#define CTX_MOVEMASK 10
#define CTX_BTNDNMASK 11
#define CTX_BTNUPMASK 12
#define CTX_INORGX 13
#define CTX_INORGY 14
#define CTX_INORGZ 15
#define CTX_INEXTX 16
#define CTX_INEXTY 17
#define CTX_INEXTZ 18
#define CTX_OUTORGX 19
#define CTX_OUTORGY 20
#define CTX_OUTORGZ 21
#define CTX_OUTEXTX 22
#define CTX_OUTEXTY 23
#define CTX_OUTEXTZ 24
#define CTX_SENSX 25
#define CTX_SENSY 26
#define CTX_SENSZ 27
#define CTX_SYSMODE 28
#define CTX_SYSORGX 29
#define CTX_SYSORGY 30
#define CTX_SYSEXTX 31
#define CTX_SYSEXTY 32
#define CTX_SYSSENSX 33
#define CTX_SYSSENSY 34
#define CTX_MAX 34
// Context option values
#define CXO_SYSTEM 0x0001
#define CXO_PEN 0x0002
#define CXO_MESSAGES 0x0004
#define CXO_MARGIN 0x8000
#define CXO_MGNINSIDE 0x4000
#define CXO_CSRMESSAGES 0x0008 /* 1.1 */
#define DVC_NAME 1
#define DVC_HARDWARE 2
#define DVC_NCSRTYPES 3
#define DVC_FIRSTCSR 4
#define DVC_PKTRATE 5
#define DVC_PKTDATA 6
#define DVC_PKTMODE 7
#define DVC_CSRDATA 8
#define DVC_XMARGIN 9
#define DVC_YMARGIN 10
#define DVC_ZMARGIN 11
#define DVC_X 12
#define DVC_Y 13
#define DVC_Z 14
#define DVC_NPRESSURE 15
#define DVC_TPRESSURE 16
#define DVC_ORIENTATION 17
#define DVC_ROTATION 18 /* 1.1 */
#define DVC_PNPID 19 /* 1.1 */
#define DVC_MAX 19
#define PK_CONTEXT 0x0001 // reporting context
#define PK_STATUS 0x0002 // status bits
#define PK_TIME 0x0004 // time stamp
#define PK_CHANGED 0x0008 // change bit vector
#define PK_SERIAL_NUMBER 0x0010 // packet serial number
#define PK_CURSOR 0x0020 // reporting cursor
#define PK_BUTTONS 0x0040 // button information
#define PK_X 0x0080 // x axis
#define PK_Y 0x0100 // y axis
#define PK_Z 0x0200 // z axis
#define PK_NORMAL_PRESSURE 0x0400 // normal or tip pressure
#define PK_TANGENT_PRESSURE 0x0800 // tangential or barrel pressure
#define PK_ORIENTATION 0x1000 // orientation info: tilts
#define PK_ROTATION 0x2000 // rotation info; 1.1
// constants for use with pktdef.h
#define PKEXT_ABSOLUTE 1
#define PKEXT_RELATIVE 2
#define WTI_DEFCONTEXT 3
#define WTI_DEFSYSCTX 4
#define WTI_DEVICES 100
#define WTI_DDCTXS 400 /* 1.1 */
#define WTI_DSCTXS 500 /* 1.1 */
#endif // Flags
typedef struct tagAXIS {
LONG axMin;
LONG axMax;
UINT axUnits;
FIX32 axResolution;
} AXIS, *PAXIS, NEAR *NPAXIS, FAR *LPAXIS;
#define LCNAMELEN 40
typedef struct tagLOGCONTEXTA {
char lcName[LCNAMELEN];
UINT lcOptions;
UINT lcStatus;
UINT lcLocks;
UINT lcMsgBase;
UINT lcDevice;
UINT lcPktRate;
WTPKT lcPktData;
WTPKT lcPktMode;
WTPKT lcMoveMask;
DWORD lcBtnDnMask;
DWORD lcBtnUpMask;
LONG lcInOrgX;
LONG lcInOrgY;
LONG lcInOrgZ;
LONG lcInExtX;
LONG lcInExtY;
LONG lcInExtZ;
LONG lcOutOrgX;
LONG lcOutOrgY;
LONG lcOutOrgZ;
LONG lcOutExtX;
LONG lcOutExtY;
LONG lcOutExtZ;
FIX32 lcSensX;
FIX32 lcSensY;
FIX32 lcSensZ;
BOOL lcSysMode;
int lcSysOrgX;
int lcSysOrgY;
int lcSysExtX;
int lcSysExtY;
FIX32 lcSysSensX;
FIX32 lcSysSensY;
} LOGCONTEXTA, *PLOGCONTEXTA, NEAR *NPLOGCONTEXTA, FAR *LPLOGCONTEXTA;
typedef struct tagEXTENSIONBASE { /* 1.4 */
HCTX nContext;
UINT nStatus;
DWORD nTime;
UINT nSerialNumber;
} EXTENSIONBASE;
#endif // wintab.h
// -----------------------------------------------------------------------------
#define PACKETDATA PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE
#define PACKETMODE 0
// -----------------------------------------------------------------------------
// pktdef.h
// -----------------------------------------------------------------------------
#if 1
// TODO: Simplify this file if we have a fixed packet format.
// The macros here are too ugly.
#ifndef PACKETNAME
/* if no packet name prefix */
#define __PFX(x) x
#define __IFX(x,y) x ## y
#else
/* add prefixes and infixes to packet format names */
#define __PFX(x) __PFX2(PACKETNAME,x)
#define __PFX2(p,x) __PFX3(p,x)
#define __PFX3(p,x) p ## x
#define __IFX(x,y) __IFX2(x,PACKETNAME,y)
#define __IFX2(x,i,y) __IFX3(x,i,y)
#define __IFX3(x,i,y) x ## i ## y
#endif
#define __SFX2(x,s) __SFX3(x,s)
#define __SFX3(x,s) x ## s
#define __TAG __IFX(tag,PACKET)
#define __TYPES __PFX(PACKET), * __IFX(P,PACKET), NEAR * __IFX(NP,PACKET), FAR * __IFX(LP,PACKET)
#define __TAGE __IFX(tag,PACKETEXT)
#define __TYPESE __PFX(PACKETEXT), * __IFX(P,PACKETEXT), NEAR * __IFX(NP,PACKETEXT), FAR * __IFX(LP,PACKETEXT)
#define __DATA (__PFX(PACKETDATA))
#define __MODE (__PFX(PACKETMODE))
#define __EXT(x) __SFX2(__PFX(PACKET),x)
typedef struct __TAG {
#if (__DATA & PK_CONTEXT)
HCTX pkContext;
#endif
#if (__DATA & PK_STATUS)
UINT pkStatus;
#endif
#if (__DATA & PK_TIME)
DWORD pkTime;
#endif
#if (__DATA & PK_CHANGED)
WTPKT pkChanged;
#endif
#if (__DATA & PK_SERIAL_NUMBER)
UINT pkSerialNumber;
#endif
#if (__DATA & PK_CURSOR)
UINT pkCursor;
#endif
#if (__DATA & PK_BUTTONS)
DWORD pkButtons;
#endif
#if (__DATA & PK_X)
LONG pkX;
#endif
#if (__DATA & PK_Y)
LONG pkY;
#endif
#if (__DATA & PK_Z)
LONG pkZ;
#endif
#if (__DATA & PK_NORMAL_PRESSURE)
#if (__MODE & PK_NORMAL_PRESSURE)
/* relative */
int pkNormalPressure;
#else
/* absolute */
UINT pkNormalPressure;
#endif
#endif
#if (__DATA & PK_TANGENT_PRESSURE)
#if (__MODE & PK_TANGENT_PRESSURE)
/* relative */
int pkTangentPressure;
#else
/* absolute */
UINT pkTangentPressure;
#endif
#endif
#if (__DATA & PK_ORIENTATION)
ORIENTATION pkOrientation;
#endif
#if (__DATA & PK_ROTATION)
ROTATION pkRotation; /* 1.1 */
#endif
#ifndef NOWTEXTENSIONS
/* extensions begin here. */
#if (__EXT(FKEYS) == PKEXT_RELATIVE) || (__EXT(FKEYS) == PKEXT_ABSOLUTE)
UINT pkFKeys;
#endif
#if (__EXT(TILT) == PKEXT_RELATIVE) || (__EXT(TILT) == PKEXT_ABSOLUTE)
TILT pkTilt;
#endif
#endif
} __TYPES;
#ifndef NOWTEXTENSIONS
typedef struct __TAGE {
EXTENSIONBASE pkBase;
#if (__EXT(EXPKEYS) == PKEXT_RELATIVE) || (__EXT(EXPKEYS) == PKEXT_ABSOLUTE)
EXPKEYSDATA pkExpKeys; /* 1.4 */
#endif
#if (__EXT(TOUCHSTRIP) == PKEXT_RELATIVE) || (__EXT(TOUCHSTRIP) == PKEXT_ABSOLUTE)
SLIDERDATA pkTouchStrip; /* 1.4 */
#endif
#if (__EXT(TOUCHRING) == PKEXT_RELATIVE) || (__EXT(TOUCHRING) == PKEXT_ABSOLUTE)
SLIDERDATA pkTouchRing; /* 1.4 */
#endif
} __TYPESE;
#endif
#undef PACKETNAME
#undef __TAG
#undef __TAGE
#undef __TAG2
#undef __TYPES
#undef __TYPESE
#undef __TYPES2
#undef __DATA
#undef __MODE
#undef __PFX
#undef __PFX2
#undef __PFX3
#undef __IFX
#undef __IFX2
#undef __IFX3
#undef __SFX2
#undef __SFX3
#endif // pktdef.h
// -----------------------------------------------------------------------------
typedef UINT (WINAPI * WTINFOA) (UINT, UINT, LPVOID);
typedef HCTX (WINAPI * WTOPENA) (HWND, LPLOGCONTEXTA, BOOL);
typedef BOOL (WINAPI * WTGETA) (HCTX, LPLOGCONTEXTA);
typedef BOOL (WINAPI * WTSETA) (HCTX, LPLOGCONTEXTA);
typedef BOOL (WINAPI * WTCLOSE) (HCTX);
typedef BOOL (WINAPI * WTENABLE) (HCTX, BOOL);
typedef BOOL (WINAPI * WTPACKET) (HCTX, UINT, LPVOID);
typedef BOOL (WINAPI * WTOVERLAP) (HCTX, BOOL);
typedef BOOL (WINAPI * WTSAVE) (HCTX, LPVOID);
typedef BOOL (WINAPI * WTCONFIG) (HCTX, HWND);
typedef HCTX (WINAPI * WTRESTORE) (HWND, LPVOID, BOOL);
typedef BOOL (WINAPI * WTEXTSET) (HCTX, UINT, LPVOID);
typedef BOOL (WINAPI * WTEXTGET) (HCTX, UINT, LPVOID);
typedef BOOL (WINAPI * WTQUEUESIZESET) (HCTX, int);
typedef int (WINAPI * WTDATAPEEK) (HCTX, UINT, UINT, int, LPVOID, LPINT);
typedef int (WINAPI * WTPACKETSGET) (HCTX, int, LPVOID);
typedef HMGR (WINAPI * WTMGROPEN) (HWND, UINT);
typedef BOOL (WINAPI * WTMGRCLOSE) (HMGR);
typedef HCTX (WINAPI * WTMGRDEFCONTEXT) (HMGR, BOOL);
typedef HCTX (WINAPI * WTMGRDEFCONTEXTEX) (HMGR, UINT, BOOL);
#endif // WIN32
// -----------------------------------------------------------------------------
// Enums
// -----------------------------------------------------------------------------
/*
Use this enum in conjunction with EasyTab->Buttons to check for tablet button
presses.
e.g. To check for lower pen button press, use:
if (EasyTab->Buttons & EasyTab_Buttons_Pen_Lower)
{
// Lower button is pressed
}
*/
enum EasyTab_Buttons_
{
EasyTab_Buttons_Pen_Touch = 1 << 0, // Pen is touching tablet
EasyTab_Buttons_Pen_Lower = 1 << 1, // Lower pen button is pressed
EasyTab_Buttons_Pen_Upper = 1 << 2, // Upper pen button is pressed
};
// -----------------------------------------------------------------------------
// Structs
// -----------------------------------------------------------------------------
typedef struct
{
int32_t PosX, PosY;
float Pressure; // Range: 0.0f to 1.0f
int32_t Buttons; // Bit field. Use with the EasyTab_Buttons_ enum.
int32_t RangeX, RangeY;
int32_t MaxPressure;
#ifdef __linux__
XDevice* Device;
uint32_t MotionType;
XEventClass EventClasses[1024];
uint32_t NumEventClasses;
#endif // __linux__
#ifdef WIN32
HINSTANCE Dll;
HCTX Context;
WTINFOA WTInfoA;
WTOPENA WTOpenA;
WTGETA WTGetA;
WTSETA WTSetA;
WTCLOSE WTClose;
WTPACKET WTPacket;
WTENABLE WTEnable;
WTOVERLAP WTOverlap;
WTSAVE WTSave;
WTCONFIG WTConfig;
WTRESTORE WTRestore;
WTEXTSET WTExtSet;
WTEXTGET WTExtGet;
WTQUEUESIZESET WTQueueSizeSet;
WTDATAPEEK WTDataPeek;
WTPACKETSGET WTPacketsGet;
WTMGROPEN WTMgrOpen;
WTMGRCLOSE WTMgrClose;
WTMGRDEFCONTEXT WTMgrDefContext;
WTMGRDEFCONTEXTEX WTMgrDefContextEx;
#endif // WIN32
} EasyTabInfo;
extern EasyTabInfo* EasyTab;
// -----------------------------------------------------------------------------
// Function declarations
// -----------------------------------------------------------------------------
#if defined(__linux__)
EasyTabResult EasyTab_Load(Display* Disp, Window Win);
EasyTabResult EasyTab_HandleEvent(XEvent* Event);
void EasyTab_Unload(Display* Disp);
#elif defined(_WIN32)
EasyTabResult EasyTab_Load(HWND Window);
EasyTabResult EasyTab_Load_Ex(HWND Window,
EasyTabTrackingMode Mode,
float RelativeModeSensitivity,
int32_t MoveCursor);
EasyTabResult EasyTab_HandleEvent(HWND Window,
UINT Message,
LPARAM LParam,
WPARAM WParam);
void EasyTab_Unload();
#else
// Save some trouble when porting.
#error "Unsupported platform."
#endif // __linux__ _WIN32
// -----------------------------------------------------------------------------
#endif // EASYTAB_H
// =============================================================================
// EasyTab implementation section
// =============================================================================
#ifdef EASYTAB_IMPLEMENTATION
EasyTabInfo* EasyTab;
// -----------------------------------------------------------------------------
// Linux implementation
// -----------------------------------------------------------------------------
#ifdef __linux__
EasyTabResult EasyTab_Load(Display* Disp, Window Win)
{
EasyTab = (EasyTabInfo*)calloc(1, sizeof(EasyTabInfo)); // We want init to zero, hence calloc.
if (!EasyTab) { return EASYTAB_MEMORY_ERROR; }
int32_t Count;
XDeviceInfoPtr Devices = (XDeviceInfoPtr)XListInputDevices(Disp, &Count);
if (!Devices) { return EASYTAB_X11_ERROR; }
for (int32_t i = 0; i < Count; i++)
{
if (!strstr(Devices[i].name, "stylus") &&
!strstr(Devices[i].name, "eraser")) { continue; }
EasyTab->Device = XOpenDevice(Disp, Devices[i].id);
XAnyClassPtr ClassPtr = Devices[i].inputclassinfo;
for (int32_t j = 0; j < Devices[i].num_classes; j++)
{
#if defined(__cplusplus)
switch (ClassPtr->c_class)
#else
switch (ClassPtr->class)
#endif
{
case ValuatorClass:
{
XValuatorInfo *Info = (XValuatorInfo *)ClassPtr;
// X
if (Info->num_axes > 0)
{
int32_t min = Info->axes[0].min_value;
EasyTab->RangeX = Info->axes[0].max_value;
//printf("Max/min x values: %d, %d\n", min, EasyTab->RangeX); // TODO: Platform-print macro
}
// Y
if (Info->num_axes > 1)
{
int32_t min = Info->axes[1].min_value;
EasyTab->RangeY = Info->axes[1].max_value;
//printf("Max/min y values: %d, %d\n", min, EasyTab->RangeY);
}
// Pressure
if (Info->num_axes > 2)
{
int32_t min = Info->axes[2].min_value;
EasyTab->MaxPressure = Info->axes[2].max_value;
//printf("Max/min pressure values: %d, %d\n", min, EasyTab->MaxPressure);
}
XEventClass EventClass;
DeviceMotionNotify(EasyTab->Device, EasyTab->MotionType, EventClass);
if (EventClass)
{
EasyTab->EventClasses[EasyTab->NumEventClasses] = EventClass;
EasyTab->NumEventClasses++;
}
} break;
}
ClassPtr = (XAnyClassPtr) ((uint8_t*)ClassPtr + ClassPtr->length); // TODO: Access this as an array to avoid pointer arithmetic?
}
XSelectExtensionEvent(Disp, Win, EasyTab->EventClasses, EasyTab->NumEventClasses);
}
XFreeDeviceList(Devices);
if (EasyTab->Device != 0) { return EASYTAB_OK; }
else { return EASYTAB_X11_ERROR; }
}
EasyTabResult EasyTab_HandleEvent(XEvent* Event)
{
if (Event->type != EasyTab->MotionType) { return EASYTAB_EVENT_NOT_HANDLED; }
XDeviceMotionEvent* MotionEvent = (XDeviceMotionEvent*)(Event);
EasyTab->PosX = MotionEvent->x;
EasyTab->PosY = MotionEvent->y;
EasyTab->Pressure = (float)MotionEvent->axis_data[2] / (float)EasyTab->MaxPressure;
return EASYTAB_OK;
}
void EasyTab_Unload(Display* Disp)
{
XCloseDevice(Disp, EasyTab->Device);
free(EasyTab);
EasyTab = NULL;
}
#endif // __linux__
// -----------------------------------------------------------------------------
// Windows implementation
// -----------------------------------------------------------------------------
#ifdef WIN32
#define GETPROCADDRESS(type, func) \
EasyTab->func = (type)GetProcAddress(EasyTab->Dll, #func); \
if (!EasyTab->func) \
{ \
OutputDebugStringA("Function " #func " not found in Wintab32.dll.\n"); \
return EASYTAB_INVALID_FUNCTION_ERROR; \
}
EasyTabResult EasyTab_Load(HWND Window)
{
return EasyTab_Load_Ex(Window, EASYTAB_TRACKING_MODE_SYSTEM, 0, 1);
}
EasyTabResult EasyTab_Load_Ex(HWND Window,
EasyTabTrackingMode TrackingMode,
float RelativeModeSensitivity,
int32_t MoveCursor)
{
EasyTab = (EasyTabInfo*)calloc(1, sizeof(EasyTabInfo)); // We want init to zero, hence calloc.
if (!EasyTab) { return EASYTAB_MEMORY_ERROR; }
// Load Wintab DLL and get function addresses
{
EasyTab->Dll = LoadLibraryA("Wintab32.dll");
if (!EasyTab->Dll)
{
OutputDebugStringA("Wintab32.dll not found.\n");
return EASYTAB_DLL_LOAD_ERROR;
}
GETPROCADDRESS(WTINFOA , WTInfoA);
GETPROCADDRESS(WTOPENA , WTOpenA);
GETPROCADDRESS(WTGETA , WTGetA);
GETPROCADDRESS(WTSETA , WTSetA);
GETPROCADDRESS(WTCLOSE , WTClose);
GETPROCADDRESS(WTPACKET , WTPacket);
GETPROCADDRESS(WTENABLE , WTEnable);
GETPROCADDRESS(WTOVERLAP , WTOverlap);
GETPROCADDRESS(WTSAVE , WTSave);
GETPROCADDRESS(WTCONFIG , WTConfig);
GETPROCADDRESS(WTRESTORE , WTRestore);
GETPROCADDRESS(WTEXTSET , WTExtSet);
GETPROCADDRESS(WTEXTGET , WTExtGet);
GETPROCADDRESS(WTQUEUESIZESET , WTQueueSizeSet);
GETPROCADDRESS(WTDATAPEEK , WTDataPeek);
GETPROCADDRESS(WTPACKETSGET , WTPacketsGet);
GETPROCADDRESS(WTMGROPEN , WTMgrOpen);
GETPROCADDRESS(WTMGRCLOSE , WTMgrClose);
GETPROCADDRESS(WTMGRDEFCONTEXT , WTMgrDefContext);
GETPROCADDRESS(WTMGRDEFCONTEXTEX , WTMgrDefContextEx);
}
if (!EasyTab->WTInfoA(0, 0, NULL))
{
OutputDebugStringA("Wintab services not available.\n");
return EASYTAB_WACOM_WIN32_ERROR;
}
// Open context
{
LOGCONTEXTA LogContext = {0};
AXIS RangeX = {0};
AXIS RangeY = {0};
AXIS Pressure = {0};
EasyTab->WTInfoA(WTI_DDCTXS, 0, &LogContext);
EasyTab->WTInfoA(WTI_DEVICES, DVC_X, &RangeX);
EasyTab->WTInfoA(WTI_DEVICES, DVC_Y, &RangeY);
EasyTab->WTInfoA(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
LogContext.lcPktData = PACKETDATA; // ??
LogContext.lcOptions |= CXO_MESSAGES;
if (MoveCursor) { LogContext.lcOptions |= CXO_SYSTEM; }
LogContext.lcPktMode = PACKETMODE;
LogContext.lcMoveMask = PACKETDATA;
LogContext.lcBtnUpMask = LogContext.lcBtnDnMask;
LogContext.lcOutOrgX = 0;
LogContext.lcOutOrgY = 0;
LogContext.lcOutExtX = GetSystemMetrics(SM_CXSCREEN);
LogContext.lcOutExtY = -GetSystemMetrics(SM_CYSCREEN);
LogContext.lcSysOrgX = 0;
LogContext.lcSysOrgY = 0;
LogContext.lcSysExtX = GetSystemMetrics(SM_CXSCREEN);
LogContext.lcSysExtY = GetSystemMetrics(SM_CYSCREEN);
if (TrackingMode == EASYTAB_TRACKING_MODE_RELATIVE)
{
LogContext.lcPktMode |= PK_X | PK_Y; // TODO: Should this be included in the
// PACKETMODE macro define up top?
LogContext.lcSysMode = 1;
if (RelativeModeSensitivity > 1.0f)
{
RelativeModeSensitivity = 1.0f;
}
else if (RelativeModeSensitivity < 0.0f)
{
RelativeModeSensitivity = 0.0f;
}
// Wintab expects sensitivity to be a 32-bit fixed point number
// with the radix point between the two words. Thus, the type
// contains 16 bits to the left of the radix point and 16 bits to
// the right of it.
//
// 0x10000 Hex
// = 65,536 Decimal
// = 0000 0000 0000 0001 . 0000 0000 0000 0000 Binary
// = 1.0 Fixed Point
uint32_t Sensitivity = (uint32_t)(0x10000 * RelativeModeSensitivity);
if (MoveCursor)
{
LogContext.lcSysSensX = LogContext.lcSysSensY = Sensitivity;
}
else
{
LogContext.lcSensX = LogContext.lcSensY = Sensitivity;
}
}
EasyTab->Context = EasyTab->WTOpenA(Window, &LogContext, TRUE);
if (!EasyTab->Context)
{
OutputDebugStringA("Wintab context couldn't be opened.\n");
return EASYTAB_WACOM_WIN32_ERROR;
}
// Get tablet capabilites
{
EasyTab->MaxPressure = Pressure.axMax;
EasyTab->RangeX = RangeX.axMax;
EasyTab->RangeY = RangeY.axMax;
}
}
return EASYTAB_OK;
}
#undef GETPROCADDRESS
EasyTabResult EasyTab_HandleEvent(HWND Window, UINT Message, LPARAM LParam, WPARAM WParam)
{
PACKET Packet = { 0 };
if (Message == WT_PACKET &&
(HCTX)LParam == EasyTab->Context &&
EasyTab->WTPacket(EasyTab->Context, (UINT)WParam, &Packet))
{
POINT Point = { 0 };
Point.x = Packet.pkX;
Point.y = Packet.pkY;
ScreenToClient(Window, &Point);
EasyTab->PosX = Point.x;
EasyTab->PosY = Point.y;
EasyTab->Pressure = (float)Packet.pkNormalPressure / (float)EasyTab->MaxPressure;
EasyTab->Buttons = Packet.pkButtons;
return EASYTAB_OK;
}
return EASYTAB_EVENT_NOT_HANDLED;
}
void EasyTab_Unload()
{
if (EasyTab->Context) { EasyTab->WTClose(EasyTab->Context); }
if (EasyTab->Dll) { FreeLibrary(EasyTab->Dll); }
free(EasyTab);
EasyTab = NULL;
}
#endif // WIN32
// -----------------------------------------------------------------------------
#endif // EASYTAB_IMPLEMENTATION

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
#pragma once
#include "env.h"
#ifdef ENV_OS_ANDROID
#include <EGL/egl.h>
#include <GLES3/gl32.h>
#else
#include "GL/glew.h"
#endif
#ifdef ENV_OS_ANDROID
#define GLW_CONTEXT_DEPTH_BITS 16
#define GLW_CONTEXT_DEPTH_COMPONENT GL_DEPTH_COMPONENT16
#else
#define GLW_CONTEXT_DEPTH_BITS 32
#define GLW_CONTEXT_DEPTH_COMPONENT GL_DEPTH_COMPONENT32
#endif

View file

@ -0,0 +1,53 @@
#pragma once
#include "rect.h"
#include "color.h"
namespace tp {
namespace glw {
extern bool gInTransition;
struct AnimValue {
halnf mValPrev = 0;
halnf mVal = 0;
time_ms mTimeStart = 0;
halni mTimeAnim = 250;
halnf interpolate() const;
AnimValue();
AnimValue(halnf val);
halnf prev() { return mValPrev; }
void set(halnf val);
void setNoTransition(halnf);
void setAnimTime(halni time);
halnf get() const;
halnf getTarget() const;
bool inTransition() const;
explicit operator halnf() const;
void operator=(halnf val);
};
struct AnimRect : rect<AnimValue> {
AnimRect() {
set({ 0, 0, 0, 0 });
setAnimTime(450);
}
rectf get() const;
rectf getTarget() const;
void setNoTransition(rectf);
void set(const rectf&);
void setAnimTime(halni time_ms);
};
struct AnimColor {
AnimRect mColor;
rgba get() const;
void set(const rgba&);
};
};
};

View file

@ -0,0 +1,84 @@
#pragma once
#include "stringt.h"
#include "fbuffer.h"
namespace tp {
namespace glw {
class Canvas {
typedef rectf Rect;
typedef vec2f Vec2;
typedef rgba Col;
typedef tp::halnf Val;
Col mColorPrimary;
Col mColorSecondary;
void* mCtx = NULL;
tp::halnf mDpmm = 1;
tp::halnf mUIScale = 1;
public:
Rect mClamping;
Rect mVieport;
enum Align : uint4 {
LEFT = 1 << 0, // Default, horizontally to left.
CENTER = 1 << 1, // horizontally to center.
RIGHT = 1 << 2, // horizontally to right.
TOP = 1 << 3, // vertically to top.
MIDDLE = 1 << 4, // vertically to middle.
BOTTOM = 1 << 5, // vertically to bottom.
BASELINE = 1 << 6, // Default, vertically to baseline.
CENTER_MIDDLE = CENTER | MIDDLE,
LEFT_MIDDLE = LEFT | MIDDLE,
};
Canvas();
Val& convert2pxls(Val&);
Rect& convert2pxls(Rect&);
Vec2& convert2pxls(Vec2&);
Val as2pxls(Val);
// Canvas Manipulation
void beginDraw(Rect viewport, tp::halnf dpmm, tp::halnf uiscale);
void clear(Col color = 0);
void endDraw();
void setClamping(Rect rec);
void resetViewport();
// Params control
void setCol1(const Col& col);
void setCol2(const Col& col);
// Shapes
void rect(Rect rec, halnf rounding = 0, halnf borders = 0);
void circle(Vec2 rec, halnf radius, halnf borders = 0);
void trig(Vec2 p1, Vec2 p2, Vec2 p3);
void line(Vec2 p1, Vec2 p2, halnf thikness = 2);
// Text
void text(const string text, Rect rec, halnf size, Align align = Align::CENTER_MIDDLE);
void text(const char* start, const char* end, Rect rec, halnf size, Align align = Align::CENTER_MIDDLE);
void drawColorwheel(Rect rec, const rgb& col);
// image
struct ImageHandle {
halni mId = NULL;
void createFromBuff(glw::fbuffer* buff, Canvas* drawer);
void free(Canvas* drawer);
~ImageHandle();
};
void drawImage(Rect rec, ImageHandle* image, halnf angle = 0.f, halnf alpha = 1.f, halnf rounding = 0.f);
~Canvas();
};
};
};

View file

@ -0,0 +1,48 @@
#include "window.h"
namespace ImGui {
bool SubMenuBegin(const char* desc, int level);
void SubMenuEnd(int level);
void ToolTip(const char* desc);
struct PopupData {
bool opened = false;
bool ishovered = false;
tp::vec2f p1 = 0;
tp::vec2f p2 = 0;
operator bool() {
return opened;
}
};
PopupData HoverPopupBeginButton(const char* id, tp::vec2f butsize = 200, tp::vec2f popupsize = 200);
PopupData HoverPopupBegin(const char* id, tp::vec2f size = 200, tp::vec2f pos = -1, int flags = 0);
void HoverPopupEnd(PopupData& in);
void ApplyStyle(tp::halnf dpmm, float font_size_mm = 5, float ui_scale = 1);
};
namespace tp {
namespace glw {
typedef void (DebugUiDrawCallBack)(void* cd);
struct DebugUI {
tp::halnf mDPMM = 1;
tp::halnf mFontSizeMM = 3;
tp::halnf mUIScale = 1;
DebugUiDrawCallBack* cb = NULL;
void* cd = NULL;
struct DebugUiInternalContext* mCtx = NULL;
DebugUI(Window& window, DebugUiDrawCallBack* acb, void* acd);
void drawDebugUI(tp::halnf dpmm);
~DebugUI();
};
};
};

View file

@ -0,0 +1,40 @@
#pragma once
#include "rect.h"
#include "color.h"
namespace tp {
namespace glw {
class fbuffer {
uint4 mFrameBufferID = 0; // regroups 0, 1, or more textures, and 0 or 1 depth buffer.
uint4 mTextureId = 0; // texture we're going to render to ( colour attachement #0 )
uint4 mDepthBufferID = 0;
uint4 mDrawBuffers[1];
vec2f mSize;
public:
rgba mClearCol = 0.f;
fbuffer(const vec2f& size);
fbuffer(const vec2f& size, tp::uint1 samples);
void beginDraw();
void setViewport(const rectf& viewport);
void clear();
void endDraw();
uint4 texId() const;
uint4 buffId() const;
const vec2f& getSize() const;
uhalni sizeAllocatedMem() const;
uhalni sizeUsedMem() const;
~fbuffer();
};
};
};

View file

@ -0,0 +1,167 @@
#pragma once
namespace tp {
// basically copy of glfw keys
enum class Keycode {
/* Printable keys */
SPACE = 32,
APOSTROPHE = 39, /* ' */
COMMA = 44, /* , */
MINUS = 45, /* - */
PERIOD = 46, /* . */
SLASH = 0xBF, /* \ */
N0 = 48,
N1 = 49,
N2 = 50,
N3 = 51,
N4 = 52,
N5 = 53,
N6 = 54,
N7 = 55,
N8 = 56,
N9 = 57,
SEMICOLON = 59, /* ; */
EQUAL = 61, /* = */
A = 65,
B = 66,
C = 67,
D = 68,
E = 69,
F = 70,
G = 71,
H = 72,
I = 73,
J = 74,
K = 75,
L = 76,
M = 77,
N = 78,
O = 79,
P = 80,
Q = 81,
R = 82,
S = 83,
T = 84,
U = 85,
V = 86,
W = 87,
X = 88,
Y = 89,
Z = 90,
LEFT_BRACKET = 91, /* [ */
BACKSLASH = 92, /* \ */
RIGHT_BRACKET = 93, /* ] */
GRAVE_ACCENT = 96, /* ` */
WORLD_1 = 161, /* non-US #1 */
WORLD_2 = 162, /* non-US #2 */
/* function keys */
ESCAPE = 256,
ENTER = 257,
TAB = 258,
BACKSPACE = 259,
INSERT = 260,
DELETE = 261,
RIGHT = 262,
LEFT = 263,
DOWN = 264,
UP = 265,
PAGE_UP = 266,
PAGE_DOWN = 267,
HOME = 268,
END = 269,
CAPS_LOCK = 280,
SCROLL_LOCK = 281,
NUM_LOCK = 282,
PRINT_SCREEN = 283,
PAUSE = 284,
F1 = 290,
F2 = 291,
F3 = 292,
F4 = 293,
F5 = 294,
F6 = 295,
F7 = 296,
F8 = 297,
F9 = 298,
F10 = 299,
F11 = 300,
F12 = 301,
F13 = 302,
F14 = 303,
F15 = 304,
F16 = 305,
F17 = 306,
F18 = 307,
F19 = 308,
F20 = 309,
F21 = 310,
F22 = 311,
F23 = 312,
F24 = 313,
F25 = 314,
KP_0 = 320,
KP_1 = 321,
KP_2 = 322,
KP_3 = 323,
KP_4 = 324,
KP_5 = 325,
KP_6 = 326,
KP_7 = 327,
KP_8 = 328,
KP_9 = 329,
KP_DECIMAL = 330,
KP_DIVIDE = 331,
KP_MULTIPLY = 332,
KP_SUBTRACT = 333,
KP_ADD = 334,
KP_ENTER = 335,
KP_EQUAL = 336,
LEFT_SHIFT = 340,
LEFT_CONTROL = 341,
LEFT_ALT = 342,
LEFT_SUPER = 343,
RIGHT_SHIFT = 344,
RIGHT_CONTROL = 345,
RIGHT_ALT = 346,
RIGHT_SUPER = 347,
MENU = 348,
INV_SLASH = 0xDC,
BRA = 0xDB,
KET = 0xDD,
TILDA = 0xC0,
DOUBLE_DOT = 0xBA,
QUOTES = 0xDE,
MOUSE1 = 501,
MOUSE2 = 502,
MOUSE3 = 503,
MOUSE4 = 504,
MOUSE5 = 505,
MOUSE_UP = 506,
MOUSE_DOWN = 507,
};
enum class Keystate {
PRESSED,
RELEASED,
HOLD,
REPEAT,
NONE,
};
struct KeyEvent {
Keycode code;
enum class EventState {
RELEASED = 0,
PRESSED = 1,
REPEAT = 2,
} event_state;
};
};

View file

@ -0,0 +1,36 @@
#pragma once
namespace tp {
namespace glw {
class shader {
uint4 programm;
uint4 VertexShaderID;
uint4 FragmentShaderID;
uint4 GeometryShaderID;
public:
bool compile_shader(const char* ShaderCode, uint4 ShaderID);
void load(const char* vert, const char* geom, const char* frag, bool paths);
shader();
void vert_bind_source(const char* vert_src);
void frag_bind_source(const char* frag_src);
void geom_bind_source(const char* geom_src);
void compile();
shader(const char* vertid, const char* geomid, const char* fragid, bool paths = true);
void bind();
uint4 getu(const char* uid);
void unbind();
~shader();
alni sizeAllocatedMem();
alni sizeUsedMem();
};
};
};

View file

@ -0,0 +1,71 @@
#pragma once
#include "window.h"
#include "canvas.h"
#include "animations.h"
namespace tp {
namespace glw {
class WindowSimpleInputs {
enum class Mouse : uint1 { NONE, PRESSED, HOLD, RELEASED } mMouse;
enum class Move : uint1 { NONE, LEFT, RIGHT, UP, DOWN } mMove;
public:
halnf mScaleVal = 0;
vec2f mWindowSizeMM;
vec2f mCrs;
vec2f mCrsPrev;
vec2f mCrsmDelta;
halnf mPressure = 0.f;
void update(const glw::Window& window, halnf uiscale);
bool Activated();
bool Anticipating();
bool Selected();
bool MoveLeft();
bool MoveRight();
bool MoveUp();
bool MoveDown();
bool Drag();
};
struct ButtonWidget {
rectf mRect = { 0.f, 10.f };
rgba mCol = { 0.3f, 0.3f, 0.3f, 1.f };
bool mPressed = false;
AnimRect mAnimRec;
void draw(glw::Canvas& canvas);
void proc(glw::WindowSimpleInputs& inputs);
};
struct CheckBoxWidget {
rectf mRect = { 0.f, 10.f };
rgba mCol = { 0.3f, 0.3f, 0.3f, 1.f };
rgba mColActive = { 0.5f, 0.5f, 0.5f, 1.f };
bool mValue = false;
AnimRect mAnimRec;
AnimColor mAnimCol;
void draw(glw::Canvas& canvas);
void proc(glw::WindowSimpleInputs& inputs);
};
struct WindowsHeaderWidget {
rectf header_rec = { 10, 10, 50, 10 };
rgba col = { 0.13f, 0.13f, 0.13f, 0.9f };
rectf grab_rec = 0.f;
ButtonWidget mExitButton;
ButtonWidget mMaxButton;
ButtonWidget mMinButton;
CheckBoxWidget mPinButton;
void updRecs();
void draw(glw::Canvas& canvas);
void proc(glw::Window& window, glw::WindowSimpleInputs& inputs);
};
};
};

View file

@ -0,0 +1,31 @@
#pragma once
#include "array2d.h"
#include "color.h"
#include "vec.h"
namespace tp {
namespace glw {
class texture {
uint4 id;
public:
texture();
~texture();
uint4 getid();
void update(const Array2D<rgba>& buff);
void draw(const uint4& out = 0);
alni sizeAllocatedMem();
alni sizeUsedMem();
static void init();
static void deinit();
static void draw_texture(uint4 out, uint4 in);
static uint4 get_tex(const char* TexId);
static void drawCurcle(vec2f pos, double radius, rgba col);
};
};
};

View file

@ -0,0 +1,114 @@
#pragma once
#include "common.h"
#include "vec.h"
#include "rect.h"
#include "color.h"
#include "stringt.h"
#include "array.h"
#include "map.h"
#include "keycodes.h"
#include "queue.h"
namespace tp {
extern tp::ModuleManifest gModuleGlw;
namespace glw {
struct PlatformContext;
class Window {
public:
struct Device {
time_ms FrameRate = NULL; // monitor fps
vec2f Size; // size of the monitor
tp::halnf mDPMM = 1;
};
struct Events {
tp::Queue<KeyEvent> mKeysQueue;
tp::uhalni mRedraw = 2;
bool mClose = false;
vec2f mCursor = 0;
halnf mPressure = 1.f;
vec2f mCursorPrev = 0.f;
halnf mPressurePrev = 1.f;
};
struct Appearence {
vec2f mPos = { 100.f, 100.f }; // global position
vec2f mSize = { 300.f, 300.f }; // width and height of window
vec2f mSizeMin = { 100.f, 100.f }; // min size possiable
vec2f mSizeMax = { 700.f, 700.f }; // max size possiable
bool mHiden = true;
bool mMaximized = false;
bool mMinimized = false;
bool mTopZ = false;
bool mAllDesktops = false;
rgba mResizeColor = rgba(0.13f, 0.13f, 0.13f, 0.9f);
rectf mMinMaxBox;
tp::Array<rectf> mCaptionsAddArea;
tp::Array<rectf> mCaptionsRemoveArea;
};
public:
PlatformContext* mPlatformCtx = NULL;
void platformCallback();
void* platform_window() const;
struct NativeEventListenerCallback {
typedef void (call_back_func)(PlatformContext* ctx, void* cd);
call_back_func* exec_begin = NULL;
call_back_func* exec = NULL;
call_back_func* exec_end = NULL;
void* cd = NULL;
};
tp::HashMap<NativeEventListenerCallback, string> mNativeEventListeners;
private:
struct Cache {
Appearence mAppearencePrev;
vec2f mWindowSize = 0;
rectf mRectBeforResizing = 0;
bool mUserResizing = false;
bool mMinMaxHover = false;
bool mMouseTracked = false;
} mCache;
void applyAppearance();
void initialize();
public:
static Device mDevice;
Appearence mAppearence;
Events mEvents;
Window();
Window(const Appearence& aAppearence);
void pollEvents();
void beginDraw();
void endDraw();
alni sizeAllocatedMem();
alni sizeUsedMem();
~Window();
};
};
};

Binary file not shown.

View file

@ -0,0 +1,25 @@
#pragma once
#include "queue.h"
struct AndroidApi {
union Event {
enum Type : tp::uint1 {
EV_NONE = 0,
} type = EV_NONE;
struct None {
} none;
};
tp::Queue<Event> events;
};
extern AndroidApi* gAndroidApi;

View file

@ -0,0 +1,804 @@
// Copyright (C) 2010 The Android Open Source Project
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// own headers
#include "window.h"
#include "GraphicsLibApi.h"
// STL
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <stdlib.h>
// jni
//#include <initializer_list>
//#include <memory>
//#include <cstdlib>
//#include <cstring>
#include <jni.h>
//#include <cerrno>
//#include <cassert>
// opengl
#include <EGL/egl.h>
#include <GLES3/gl32.h>
//#include "GL/glew.h"
// glue
#include <android/sensor.h>
#include <android/log.h>
#include <android/input.h>
#include <../../../android/native_app_glue/android_native_app_glue.h>
// AcquireASensorManagerInstance
#include <dlfcn.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__))
// application side entry point
int main();
// first function to be called in "main thread"
void* main_wrap(void* p);
// AcquireASensorManagerInstance(void)
// Workaround ASensorManager_getInstance() deprecation false alarm
// for Android-N and before, when compiling with NDK-r15
ASensorManager* AcquireASensorManagerInstance(android_app* app) {
if (!app)
return nullptr;
typedef ASensorManager* (*PF_GETINSTANCEFORPACKAGE)(const char* name);
void* androidHandle = dlopen("libandroid.so", RTLD_NOW);
auto getInstanceForPackageFunc = (PF_GETINSTANCEFORPACKAGE)
dlsym(androidHandle, "ASensorManager_getInstanceForPackage");
if (getInstanceForPackageFunc) {
JNIEnv* env = nullptr;
app->activity->vm->AttachCurrentThread(&env, nullptr);
jclass android_content_Context = env->GetObjectClass(app->activity->clazz);
jmethodID midGetPackageName = env->GetMethodID(android_content_Context,
"getPackageName",
"()Ljava/lang/String;");
auto packageName = (jstring)env->CallObjectMethod(app->activity->clazz,
midGetPackageName);
const char* nativePackageName = env->GetStringUTFChars(packageName, nullptr);
ASensorManager* mgr = getInstanceForPackageFunc(nativePackageName);
env->ReleaseStringUTFChars(packageName, nativePackageName);
app->activity->vm->DetachCurrentThread();
if (mgr) {
dlclose(androidHandle);
return mgr;
}
}
typedef ASensorManager* (*PF_GETINSTANCE)();
auto getInstanceFunc = (PF_GETINSTANCE)
dlsym(androidHandle, "ASensorManager_getInstance");
// by all means at this point, ASensorManager_getInstance should be available
assert(getInstanceFunc);
dlclose(androidHandle);
return getInstanceFunc();
}
void GL_APIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
DBG_BREAK(true);
LOGW("GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message);
}
char const* gl_error_string(GLenum const err) {
switch (err) {
case GL_NO_ERROR: return "GL_NO_ERROR";
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
default: return "unknown error";
}
}
void glerr(GLenum type) {
DBG_BREAK(true);
const GLchar* message = gl_error_string(type);
LOGW("GL CALLBACK: %s type = 0x%x, message = %s\n", (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, message);
}
void glerr(GLenum type);
#define AssertGL(x) { x; GLenum __gle = glGetError(); if (__gle != GL_NO_ERROR) glerr(__gle); }
// --------------------------------- Data --------------------------------------- //
struct MutexEvent {
pthread_mutex_t mutex;
tp::glw::Window* active_windw;
bool parent_thread_has_events = false;
bool child_whaiting = false;
} mutex_event;
struct ANativeActivityCtx* android_ctx = NULL;
tp::glw::Window::Device tp::glw::Window::mDevice;
extern const char* g_android_internal_data_path;
static pthread_t worker_thread;
static pthread_mutex_t worker_finished_mutex;
static pthread_cond_t worker_finished_cond;
static bool worker_finished = false;
namespace tp {
namespace glw {
struct PlatformContext {
int col = 255;
EGLint w, h, format;
EGLint numConfigs;
EGLConfig config = nullptr;
EGLSurface surface;
EGLContext context;
EGLDisplay display = nullptr;
AInputEvent* chache_event = NULL;
EGLint majorVersion, minorVersion;
// Initialize an EGL context for the current display.
PlatformContext(ANativeWindow* window) {
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (EGL_NO_DISPLAY == display) {
return;
}
if (eglInitialize(display, &majorVersion, &minorVersion) != EGL_TRUE) {
return;
}
// Here specify the attributes of the desired configuration.
// Below, we select an EGLConfig with at least 8 bits per color component compatible with on-screen windows
const EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, // request OpenGL ES 3.0
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, GLW_CONTEXT_DEPTH_BITS,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
// Here, the application chooses the configuration it desires.
// find the best match if possible, otherwise use the very first one
{
if (eglChooseConfig(display, attribs, nullptr, 0, &numConfigs) != EGL_TRUE) {
return;
}
std::unique_ptr<EGLConfig[]> supportedConfigs(new EGLConfig[numConfigs]);
assert(supportedConfigs);
if (eglChooseConfig(display, attribs, supportedConfigs.get(), numConfigs, &numConfigs) != EGL_TRUE) {
return;
}
assert(numConfigs);
auto i = 0;
for (; i < numConfigs; i++) {
auto& cfg = supportedConfigs[i];
EGLint r, g, b, d;
if (eglGetConfigAttrib(display, cfg, EGL_RED_SIZE, &r) &&
eglGetConfigAttrib(display, cfg, EGL_GREEN_SIZE, &g) &&
eglGetConfigAttrib(display, cfg, EGL_BLUE_SIZE, &b) &&
eglGetConfigAttrib(display, cfg, EGL_DEPTH_SIZE, &d) &&
r == 8 && g == 8 && b == 8 && d == GLW_CONTEXT_DEPTH_BITS) {
config = supportedConfigs[i];
break;
}
}
if (i == numConfigs) {
config = supportedConfigs[0];
}
if (config == nullptr) {
LOGW("Unable to initialize EGLConfig");
return;
}
}
const EGLint egl_context_attributes[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
//EGL_CONTEXT_MAJOR_VERSION, 3,
//EGL_CONTEXT_MINOR_VERSION, 0,
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
#ifdef ENV_BUILD_DEBUG
//EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE,
#endif
EGL_NONE
};
// EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
// guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
// As soon as we picked a EGLConfig, we can safely reconfigure the
// ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
surface = eglCreateWindowSurface(display, config, window, nullptr);
context = eglCreateContext(display, config, EGL_NO_CONTEXT, egl_context_attributes);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
LOGW("Unable to eglMakeCurrent");
return;
}
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
// Check openGL on the system
auto opengl_info = { GL_VENDOR, GL_RENDERER, GL_VERSION, GL_EXTENSIONS };
for (auto name : opengl_info) {
auto info = glGetString(name);
LOGI("OpenGL Info: %s", info);
}
// Initialize GL state.
//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
AssertGL(glEnable(GL_CULL_FACE));
//AssertGL(glShadeModel(GL_SMOOTH));
AssertGL(glEnable(GL_DEPTH_TEST));
AssertGL(glDepthFunc(GL_LESS));
GLint major = 0;
GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
glGetIntegerv(GL_DEPTH, &minor);
AssertGL(glEnable(GL_DEBUG_OUTPUT));
AssertGL(glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS));
AssertGL(glDebugMessageCallback(MessageCallback, 0));
tp::glw::Window::mDevice.Size = { w, h };
tp::glw::Window::mDevice.FrameRate = 60;
return;
}
void beginDraw() {
if (this->display == nullptr) { return; }
glViewport(0, 0, tp::glw::Window::mDevice.Size.x, tp::glw::Window::mDevice.Size.y);
//glClearColor(col / 255.f, col / 255.f, col / 255.f, 1);
//glClear(GL_COLOR_BUFFER_BIT);
//if (col++ > 255) {
//col = 0;
//}
}
void endDraw() {
eglSwapBuffers(display, surface);
}
// Tear down the EGL context currently associated with the display.
~PlatformContext() {
if (this->display != EGL_NO_DISPLAY) {
eglMakeCurrent(this->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (this->context != EGL_NO_CONTEXT) {
eglDestroyContext(this->display, this->context);
}
if (this->surface != EGL_NO_SURFACE) {
eglDestroySurface(this->display, this->surface);
}
eglTerminate(this->display);
}
display = EGL_NO_DISPLAY;
context = EGL_NO_CONTEXT;
surface = EGL_NO_SURFACE;
}
};
};
};
// ---------------------------------- Parent Thread ------------------------------------ //
#ifndef PARENT_THREAD
// Shared state for our app.
struct ANativeActivityCtx {
// Our saved state data.
struct saved_state {
float angle;
int32_t x;
int32_t y;
};
android_app* app;
ASensorManager* sensorManager;
const ASensor* accelerometerSensor;
ASensorEventQueue* sensorEventQueue;
int animating;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
int32_t width;
int32_t height;
saved_state state;
ANativeActivityCtx(android_app* state) {
memset(this, 0, sizeof(*this));
state->userData = this;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
app = state;
// Prepare to monitor accelerometer
sensorManager = AcquireASensorManagerInstance(state);
accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
sensorEventQueue = ASensorManager_createEventQueue(sensorManager, state->looper, LOOPER_ID_USER, 0, 0);
// We are starting with a previous saved state; restore from it.
if (state->savedState != nullptr) {
this->state = *(saved_state*)state->savedState;
}
}
void proc_events_util() {
int ident;
int events;
struct android_poll_source* source;
auto window = mutex_event.active_windw;
if (window) {
for (auto proc : window->mNativeEventListeners) {
proc->val.exec_begin(window->mPlatformCtx, proc->val.cd);
}
}
// we loop until all events are read
while ((ident = ALooper_pollAll(0, nullptr, &events, (void**)&source)) >= 0) {
// Process this event.
if (source != nullptr) {
source->process(this->app, source);
}
// If a sensor has data, process it now.
if (ident == LOOPER_ID_USER) {
if (this->accelerometerSensor != nullptr) {
ASensorEvent event;
while (ASensorEventQueue_getEvents(this->sensorEventQueue, &event, 1) > 0) {
LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z);
}
}
}
// Check if we are exiting.
if (this->app->destroyRequested != 0) {
DBG_BREAK(1);
}
}
if (window) {
for (auto proc : window->mNativeEventListeners) {
proc->val.exec_end(window->mPlatformCtx, proc->val.cd);
}
}
}
void proc_events() {
pthread_mutex_lock(&mutex_event.mutex);
auto threaded = mutex_event.active_windw;
pthread_mutex_unlock(&mutex_event.mutex);
if (!threaded) {
proc_events_util();
return;
}
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
// check if events exist and if so prepare to proccess them
if ((ident = ALooper_pollAll(0, nullptr, &events, (void**)&source)) >= 0) {
// signal child thread that we need to process those events
pthread_mutex_lock(&mutex_event.mutex);
mutex_event.parent_thread_has_events = true;
pthread_mutex_unlock(&mutex_event.mutex);
// whait until child thread pauses
bool whait = true;
while (whait) {
pthread_mutex_lock(&mutex_event.mutex);
whait = !mutex_event.child_whaiting;
pthread_mutex_unlock(&mutex_event.mutex);
}
}
else {
return;
}
pthread_mutex_lock(&mutex_event.mutex);
auto window = mutex_event.active_windw;
pthread_mutex_unlock(&mutex_event.mutex);
proc_events_util();
// signal to child continue to do its work
pthread_mutex_lock(&mutex_event.mutex);
mutex_event.parent_thread_has_events = false;
pthread_mutex_unlock(&mutex_event.mutex);
}
// Process the next input event.
static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {
auto* engine = (struct ANativeActivityCtx*)app->userData;
auto window = mutex_event.active_windw;
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
window->mEvents.mCursorPrev = window->mEvents.mCursor;
window->mEvents.mCursor.x = AMotionEvent_getX(event, 0);
window->mEvents.mCursor.y = AMotionEvent_getY(event, 0);
window->mEvents.mRedraw = 2;
int32_t event_action = AMotionEvent_getAction(event);
int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
event_action &= AMOTION_EVENT_ACTION_MASK;
if ((AMotionEvent_getToolType(event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER)
|| (AMotionEvent_getToolType(event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) {
if (event_action == AMOTION_EVENT_ACTION_DOWN) {
window->mEvents.mKeysQueue.push(tp::KeyEvent{ tp::Keycode::MOUSE1, tp::KeyEvent::EventState::PRESSED });
}
else if (event_action == AMOTION_EVENT_ACTION_UP) {
window->mEvents.mKeysQueue.push(tp::KeyEvent{ tp::Keycode::MOUSE1, tp::KeyEvent::EventState::RELEASED });
}
}
}
if (window) {
window->mPlatformCtx->chache_event = event;
for (auto proc : window->mNativeEventListeners) {
proc->val.exec(window->mPlatformCtx, proc->val.cd);
}
}
return 0;
}
// Process the next main command.
static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
auto* engine = (struct ANativeActivityCtx*)app->userData;
auto window = mutex_event.active_windw;
switch (cmd) {
// The system has asked us to save our current state. Do so.
case APP_CMD_SAVE_STATE: {
engine->app->savedState = malloc(sizeof(ANativeActivityCtx::saved_state));
*((ANativeActivityCtx::saved_state*)engine->app->savedState) = engine->state;
engine->app->savedStateSize = sizeof(ANativeActivityCtx::saved_state);
} break;
// The window is being shown, get it ready.
case APP_CMD_INIT_WINDOW: {
if (engine->app->window != nullptr) {
//engine->init_display();
//engine->draw_frame();
}
} break;
// The window is being hidden or closed, clean it up.
case APP_CMD_TERM_WINDOW: {
//engine->term_display();
} break;
// When our app gains focus, we start monitoring the accelerometer.
case APP_CMD_GAINED_FOCUS: {
if (engine->accelerometerSensor != nullptr) {
ASensorEventQueue_enableSensor(engine->sensorEventQueue,
engine->accelerometerSensor);
// We'd like to get 60 events per second (in us).
ASensorEventQueue_setEventRate(engine->sensorEventQueue,
engine->accelerometerSensor,
(1000L / 60) * 1000);
}
} break;
// When our app loses focus, we stop monitoring the accelerometer.
// This is to avoid consuming battery while not being used.
case APP_CMD_LOST_FOCUS: {
if (engine->accelerometerSensor != nullptr) {
ASensorEventQueue_disableSensor(engine->sensorEventQueue,
engine->accelerometerSensor);
}
// Also stop animating.
engine->animating = 0;
//engine->draw_frame();
} break;
default: break;
}
}
};
#include <sys/stat.h>
void unsure_dir_exist(const char* path_to_file) {
char* temp_path = strdup(path_to_file);
for (char* p = temp_path + 1; *p; p++) {
if (*p == '/') {
*p = '\0';
int status = mkdir(temp_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status != 0 && errno != EEXIST) {
// handle error
break;
}
*p = '/';
}
}
free(temp_path);
}
// FIXME
void unpackAsset(AAssetManager* mgr, const char* filename, const std::string& path) {
AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
if (asset == NULL) {
return;
}
off_t length = AAsset_getLength(asset);
char* buffer = new char[length];
AAsset_read(asset, buffer, length);
unsure_dir_exist(path.c_str());
FILE* out = fopen(path.c_str(), "w");
if (out != NULL) {
fwrite(buffer, length, 1, out);
fclose(out);
}
delete[] buffer;
AAsset_close(asset);
}
// FIXME
void unpackDirectory(AAssetManager* mgr, const char* dirname, const std::string& path) {
AAssetDir* assetDir = AAssetManager_openDir(mgr, dirname);
const char* filename = NULL;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
std::string assetPath = std::string(dirname) + "/" + filename;
std::string internalPath = path + "/" + std::string(dirname) + "/" + filename;
struct stat sb;
stat(assetPath.c_str(), &sb);
if (S_ISDIR(sb.st_mode)) {
unpackDirectory(mgr, assetPath.c_str(), internalPath);
}
else {
unpackAsset(mgr, assetPath.c_str(), internalPath);
}
}
AAssetDir_close(assetDir);
}
extern "C" {
// This is the main entry point of a native application that is using
// android_native_app_glue. It runs in its own thread, with its own
// event loop for receiving input events and doing other things.
void android_main(android_app* state) {
tp::print_env_info();
tp::alloc_init();
ANativeActivityCtx engine(state);
unpackDirectory(state->activity->assetManager, "", state->activity->internalDataPath);
unpackDirectory(state->activity->assetManager, "rsc", state->activity->internalDataPath);
unpackDirectory(state->activity->assetManager, "rsc/fonts", state->activity->internalDataPath);
android_ctx = &engine;
bool running = true;
while (running) {
engine.proc_events();
// whait until our app fully initialized
if (!engine.app->window) {
continue;
}
// run once
static bool main_thread_started = false;
if (!main_thread_started) {
// init types
g_android_internal_data_path = state->activity->internalDataPath;
// create main thread
pthread_create(&worker_thread, NULL, main_wrap, NULL);
pthread_mutex_init(&worker_finished_mutex, NULL);
pthread_cond_init(&worker_finished_cond, NULL);
main_thread_started = true;
continue;
}
// check for exit
pthread_mutex_lock(&worker_finished_mutex);
running = !worker_finished;
pthread_mutex_unlock(&worker_finished_mutex);
}
pthread_join(worker_thread, NULL);
tp::alloc_uninit();
}
};
#endif
// ------------------------------- Child thread --------------------------------------- //
#ifndef CHILD_THREAD
//#include <android/configuration.h>
//#include <android/native_activity.h>
AInputEvent* androidPlatformContextGetEvent(tp::glw::PlatformContext* ctx) {
return ctx->chache_event;
}
int32_t getDensityDpi(android_app* app) {
AConfiguration* config = AConfiguration_new();
AConfiguration_fromAssetManager(config, app->activity->assetManager);
int32_t density = AConfiguration_getDensity(config);
AConfiguration_delete(config);
return density;
}
// statics
void tp::glw::Window::init() {}
void tp::glw::Window::deinit() {}
void tp::glw::Window::initialize() {
mPlatformCtx = new PlatformContext(android_ctx->app->window);
applyAppearance();
AConfiguration* config = AConfiguration_new();
AConfiguration_fromAssetManager(config, android_ctx->app->activity->assetManager);
int32_t density = AConfiguration_getDensity(config);
AConfiguration_delete(config);
mDevice.mDPMM = density / 25.4f;
}
tp::glw::Window::Window() {
initialize();
pthread_mutex_lock(&mutex_event.mutex);
mutex_event.active_windw = this;
pthread_mutex_unlock(&mutex_event.mutex);
}
tp::glw::Window::Window(const Appearence& aAppearence) {
mAppearence = aAppearence;
initialize();
}
tp::glw::Window::~Window() {
delete mPlatformCtx;
}
void tp::glw::Window::applyAppearance() {
int width = ANativeWindow_getWidth(android_ctx->app->window);
int height = ANativeWindow_getHeight(android_ctx->app->window);
if (width != mAppearence.mSize.x || height != mAppearence.mSize.y) {
mEvents.mRedraw = 2;
}
mAppearence.mPos = mCache.mAppearencePrev.mPos = 0;
mAppearence.mSize.x = mCache.mAppearencePrev.mSize.x = tp::glw::Window::mDevice.Size.x = width;
mAppearence.mSize.y = mCache.mAppearencePrev.mSize.y = tp::glw::Window::mDevice.Size.y = height;
}
void tp::glw::Window::pollEvents() {
applyAppearance();
// check if parent thread has some events to pass
pthread_mutex_lock(&mutex_event.mutex);
bool has_event = mutex_event.parent_thread_has_events;
mutex_event.child_whaiting = has_event;
pthread_mutex_unlock(&mutex_event.mutex);
while (has_event) {
pthread_mutex_lock(&mutex_event.mutex);
has_event = mutex_event.parent_thread_has_events;
pthread_mutex_unlock(&mutex_event.mutex);
}
pthread_mutex_lock(&mutex_event.mutex);
mutex_event.child_whaiting = false;
pthread_mutex_unlock(&mutex_event.mutex);
}
void tp::glw::Window::platformCallback() {}
void tp::glw::Window::beginDraw() {
mPlatformCtx->beginDraw();
}
void tp::glw::Window::endDraw() {
mPlatformCtx->endDraw();
if (mEvents.mRedraw-- < 0) mEvents.mRedraw = 0;
}
void* tp::glw::Window::platform_window() const { return android_ctx->app->window; }
tp::alni tp::glw::Window::sizeAllocatedMem() { return 0; }
tp::alni tp::glw::Window::sizeUsedMem() { return 0; }
void* main_wrap(void* p) {
// exec main
main();
// mark as doen after completion
pthread_mutex_lock(&worker_finished_mutex);
worker_finished = true;
pthread_cond_signal(&worker_finished_cond);
pthread_mutex_unlock(&worker_finished_mutex);
// exit "main thread"
pthread_exit(NULL);
return NULL;
}
#endif

View file

@ -0,0 +1,453 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "android_native_app_glue.h"
#include <jni.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
/* For debug builds, always enable the debug traces in this library */
#ifndef NDEBUG
# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
#else
# define LOGV(...) ((void)0)
#endif
void free_saved_state(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex);
if (android_app->savedState != NULL) {
free(android_app->savedState);
android_app->savedState = NULL;
android_app->savedStateSize = 0;
}
pthread_mutex_unlock(&android_app->mutex);
}
int8_t android_app_read_cmd(struct android_app* android_app) {
int8_t cmd;
if (read(android_app->msgread, &cmd, sizeof(cmd)) != sizeof(cmd)) {
LOGE("No data on command pipe!");
return -1;
}
if (cmd == APP_CMD_SAVE_STATE) free_saved_state(android_app);
return cmd;
}
void print_cur_config(struct android_app* android_app) {
char lang[2], country[2];
AConfiguration_getLanguage(android_app->config, lang);
AConfiguration_getCountry(android_app->config, country);
LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
"keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
"modetype=%d modenight=%d",
AConfiguration_getMcc(android_app->config),
AConfiguration_getMnc(android_app->config),
lang[0], lang[1], country[0], country[1],
AConfiguration_getOrientation(android_app->config),
AConfiguration_getTouchscreen(android_app->config),
AConfiguration_getDensity(android_app->config),
AConfiguration_getKeyboard(android_app->config),
AConfiguration_getNavigation(android_app->config),
AConfiguration_getKeysHidden(android_app->config),
AConfiguration_getNavHidden(android_app->config),
AConfiguration_getSdkVersion(android_app->config),
AConfiguration_getScreenSize(android_app->config),
AConfiguration_getScreenLong(android_app->config),
AConfiguration_getUiModeType(android_app->config),
AConfiguration_getUiModeNight(android_app->config));
}
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
switch (cmd) {
case APP_CMD_INPUT_CHANGED:
LOGV("APP_CMD_INPUT_CHANGED");
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
android_app->inputQueue = android_app->pendingInputQueue;
if (android_app->inputQueue != NULL) {
LOGV("Attaching input queue to looper");
AInputQueue_attachLooper(android_app->inputQueue,
android_app->looper, LOOPER_ID_INPUT, NULL,
&android_app->inputPollSource);
}
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_INIT_WINDOW:
LOGV("APP_CMD_INIT_WINDOW");
pthread_mutex_lock(&android_app->mutex);
android_app->window = android_app->pendingWindow;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_TERM_WINDOW:
LOGV("APP_CMD_TERM_WINDOW");
pthread_cond_broadcast(&android_app->cond);
break;
case APP_CMD_RESUME:
case APP_CMD_START:
case APP_CMD_PAUSE:
case APP_CMD_STOP:
LOGV("activityState=%d", cmd);
pthread_mutex_lock(&android_app->mutex);
android_app->activityState = cmd;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_CONFIG_CHANGED:
LOGV("APP_CMD_CONFIG_CHANGED");
AConfiguration_fromAssetManager(android_app->config,
android_app->activity->assetManager);
print_cur_config(android_app);
break;
case APP_CMD_DESTROY:
LOGV("APP_CMD_DESTROY");
android_app->destroyRequested = 1;
break;
}
}
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
switch (cmd) {
case APP_CMD_TERM_WINDOW:
LOGV("APP_CMD_TERM_WINDOW");
pthread_mutex_lock(&android_app->mutex);
android_app->window = NULL;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_SAVE_STATE:
LOGV("APP_CMD_SAVE_STATE");
pthread_mutex_lock(&android_app->mutex);
android_app->stateSaved = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
break;
case APP_CMD_RESUME:
free_saved_state(android_app);
break;
}
}
void android_app_destroy(struct android_app* android_app) {
LOGV("android_app_destroy!");
free_saved_state(android_app);
pthread_mutex_lock(&android_app->mutex);
if (android_app->inputQueue != NULL) {
AInputQueue_detachLooper(android_app->inputQueue);
}
AConfiguration_delete(android_app->config);
android_app->destroyed = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
// Can't touch android_app object after this.
}
void process_input(struct android_app* app, struct android_poll_source* source) {
AInputEvent* event = NULL;
while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
LOGV("New input event: type=%d", AInputEvent_getType(event));
if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
continue;
}
int32_t handled = 0;
if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
AInputQueue_finishEvent(app->inputQueue, event, handled);
}
}
void process_cmd(struct android_app* app, struct android_poll_source* source) {
int8_t cmd = android_app_read_cmd(app);
android_app_pre_exec_cmd(app, cmd);
if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
android_app_post_exec_cmd(app, cmd);
}
void* android_app_entry(void* param) {
struct android_app* android_app = (struct android_app*)param;
android_app->config = AConfiguration_new();
AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
print_cur_config(android_app);
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
android_app->cmdPollSource.app = android_app;
android_app->cmdPollSource.process = process_cmd;
android_app->inputPollSource.id = LOOPER_ID_INPUT;
android_app->inputPollSource.app = android_app;
android_app->inputPollSource.process = process_input;
ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
&android_app->cmdPollSource);
android_app->looper = looper;
pthread_mutex_lock(&android_app->mutex);
android_app->running = 1;
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
android_main(android_app);
android_app_destroy(android_app);
return NULL;
}
// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------
struct android_app* android_app_create(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
struct android_app* android_app = calloc(1, sizeof(struct android_app));
android_app->activity = activity;
pthread_mutex_init(&android_app->mutex, NULL);
pthread_cond_init(&android_app->cond, NULL);
if (savedState != NULL) {
android_app->savedState = malloc(savedStateSize);
android_app->savedStateSize = savedStateSize;
memcpy(android_app->savedState, savedState, savedStateSize);
}
int msgpipe[2];
if (pipe(msgpipe)) {
LOGE("could not create pipe: %s", strerror(errno));
return NULL;
}
android_app->msgread = msgpipe[0];
android_app->msgwrite = msgpipe[1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
// Wait for thread to start.
pthread_mutex_lock(&android_app->mutex);
while (!android_app->running) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
return android_app;
}
void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
LOGE("Failure writing android_app cmd: %s", strerror(errno));
}
}
void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
pthread_mutex_lock(&android_app->mutex);
android_app->pendingInputQueue = inputQueue;
android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
while (android_app->inputQueue != android_app->pendingInputQueue) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
pthread_mutex_lock(&android_app->mutex);
if (android_app->pendingWindow != NULL) {
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
}
android_app->pendingWindow = window;
if (window != NULL) {
android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
}
while (android_app->window != android_app->pendingWindow) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, cmd);
while (android_app->activityState != cmd) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
}
void android_app_free(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, APP_CMD_DESTROY);
while (!android_app->destroyed) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
pthread_mutex_unlock(&android_app->mutex);
close(android_app->msgread);
close(android_app->msgwrite);
pthread_cond_destroy(&android_app->cond);
pthread_mutex_destroy(&android_app->mutex);
free(android_app);
}
struct android_app* ToApp(ANativeActivity* activity) {
return (struct android_app*) activity->instance;
}
void onDestroy(ANativeActivity* activity) {
LOGV("Destroy: %p", activity);
android_app_free(ToApp(activity));
}
void onStart(ANativeActivity* activity) {
LOGV("Start: %p", activity);
android_app_set_activity_state(ToApp(activity), APP_CMD_START);
}
void onResume(ANativeActivity* activity) {
LOGV("Resume: %p", activity);
android_app_set_activity_state(ToApp(activity), APP_CMD_RESUME);
}
void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
LOGV("SaveInstanceState: %p", activity);
struct android_app* android_app = ToApp(activity);
void* savedState = NULL;
pthread_mutex_lock(&android_app->mutex);
android_app->stateSaved = 0;
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
while (!android_app->stateSaved) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
}
if (android_app->savedState != NULL) {
savedState = android_app->savedState;
*outLen = android_app->savedStateSize;
android_app->savedState = NULL;
android_app->savedStateSize = 0;
}
pthread_mutex_unlock(&android_app->mutex);
return savedState;
}
void onPause(ANativeActivity* activity) {
LOGV("Pause: %p", activity);
android_app_set_activity_state(ToApp(activity), APP_CMD_PAUSE);
}
void onStop(ANativeActivity* activity) {
LOGV("Stop: %p", activity);
android_app_set_activity_state(ToApp(activity), APP_CMD_STOP);
}
void onConfigurationChanged(ANativeActivity* activity) {
LOGV("ConfigurationChanged: %p", activity);
android_app_write_cmd(ToApp(activity), APP_CMD_CONFIG_CHANGED);
}
void onContentRectChanged(ANativeActivity* activity, const ARect* r) {
LOGV("ContentRectChanged: l=%d,t=%d,r=%d,b=%d", r->left, r->top, r->right, r->bottom);
struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex);
android_app->contentRect = *r;
pthread_mutex_unlock(&android_app->mutex);
android_app_write_cmd(ToApp(activity), APP_CMD_CONTENT_RECT_CHANGED);
}
void onLowMemory(ANativeActivity* activity) {
LOGV("LowMemory: %p", activity);
android_app_write_cmd(ToApp(activity), APP_CMD_LOW_MEMORY);
}
void onWindowFocusChanged(ANativeActivity* activity, int focused) {
LOGV("WindowFocusChanged: %p -- %d", activity, focused);
android_app_write_cmd(ToApp(activity), focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
}
void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
LOGV("NativeWindowCreated: %p -- %p", activity, window);
android_app_set_window(ToApp(activity), window);
}
void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
LOGV("NativeWindowDestroyed: %p -- %p", activity, window);
android_app_set_window(ToApp(activity), NULL);
}
void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* window) {
LOGV("NativeWindowRedrawNeeded: %p -- %p", activity, window);
android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_REDRAW_NEEDED);
}
void onNativeWindowResized(ANativeActivity* activity, ANativeWindow* window) {
LOGV("NativeWindowResized: %p -- %p", activity, window);
android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_RESIZED);
}
void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
LOGV("InputQueueCreated: %p -- %p", activity, queue);
android_app_set_input(ToApp(activity), queue);
}
void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
LOGV("InputQueueDestroyed: %p -- %p", activity, queue);
android_app_set_input(ToApp(activity), NULL);
}
void ILOVEU_ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) {
LOGV("Creating: %p", activity);
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onContentRectChanged = onContentRectChanged;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
activity->callbacks->onNativeWindowResized = onNativeWindowResized;
activity->callbacks->onPause = onPause;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onStart = onStart;
activity->callbacks->onStop = onStop;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->instance = android_app_create(activity, savedState, savedStateSize);
}

View file

@ -0,0 +1,350 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <android/configuration.h>
#include <android/looper.h>
#include <android/native_activity.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* The native activity interface provided by <android/native_activity.h>
* is based on a set of application-provided callbacks that will be called
* by the Activity's main thread when certain events occur.
*
* This means that each one of this callbacks _should_ _not_ block, or they
* risk having the system force-close the application. This programming
* model is direct, lightweight, but constraining.
*
* The 'android_native_app_glue' static library is used to provide a different
* execution model where the application can implement its own main event
* loop in a different thread instead. Here's how it works:
*
* 1/ The application must provide a function named "android_main()" that
* will be called when the activity is created, in a new thread that is
* distinct from the activity's main thread.
*
* 2/ android_main() receives a pointer to a valid "android_app" structure
* that contains references to other important objects, e.g. the
* ANativeActivity obejct instance the application is running in.
*
* 3/ the "android_app" object holds an ALooper instance that already
* listens to two important things:
*
* - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
* declarations below.
*
* - input events coming from the AInputQueue attached to the activity.
*
* Each of these correspond to an ALooper identifier returned by
* ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
* respectively.
*
* Your application can use the same ALooper to listen to additional
* file-descriptors. They can either be callback based, or with return
* identifiers starting with LOOPER_ID_USER.
*
* 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
* the returned data will point to an android_poll_source structure. You
* can call the process() function on it, and fill in android_app->onAppCmd
* and android_app->onInputEvent to be called for your own processing
* of the event.
*
* Alternatively, you can call the low-level functions to read and process
* the data directly... look at the process_cmd() and process_input()
* implementations in the glue to see how to do this.
*
* See the sample named "native-activity" that comes with the NDK with a
* full usage example. Also look at the JavaDoc of NativeActivity.
*/
struct android_app;
/**
* Data associated with an ALooper fd that will be returned as the "outData"
* when that source has data ready.
*/
struct android_poll_source {
// The identifier of this source. May be LOOPER_ID_MAIN or
// LOOPER_ID_INPUT.
int32_t id;
// The android_app this ident is associated with.
struct android_app* app;
// Function to call to perform the standard processing of data from
// this source.
void (*process)(struct android_app* app, struct android_poll_source* source);
};
/**
* This is the interface for the standard glue code of a threaded
* application. In this model, the application's code is running
* in its own thread separate from the main thread of the process.
* It is not required that this thread be associated with the Java
* VM, although it will need to be in order to make JNI calls any
* Java objects.
*/
struct android_app {
// The application can place a pointer to its own state object
// here if it likes.
void* userData;
// Fill this in with the function to process main app commands (APP_CMD_*)
void (*onAppCmd)(struct android_app* app, int32_t cmd);
// Fill this in with the function to process input events. At this point
// the event has already been pre-dispatched, and it will be finished upon
// return. Return 1 if you have handled the event, 0 for any default
// dispatching.
int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
// The ANativeActivity object instance that this app is running in.
ANativeActivity* activity;
// The current configuration the app is running in.
AConfiguration* config;
// This is the last instance's saved state, as provided at creation time.
// It is NULL if there was no state. You can use this as you need; the
// memory will remain around until you call android_app_exec_cmd() for
// APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
// These variables should only be changed when processing a APP_CMD_SAVE_STATE,
// at which point they will be initialized to NULL and you can malloc your
// state and place the information here. In that case the memory will be
// freed for you later.
void* savedState;
size_t savedStateSize;
// The ALooper associated with the app's thread.
ALooper* looper;
// When non-NULL, this is the input queue from which the app will
// receive user input events.
AInputQueue* inputQueue;
// When non-NULL, this is the window surface that the app can draw in.
ANativeWindow* window;
// Current content rectangle of the window; this is the area where the
// window's content should be placed to be seen by the user.
ARect contentRect;
// Current state of the app's activity. May be either APP_CMD_START,
// APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
int activityState;
// This is non-zero when the application's NativeActivity is being
// destroyed and waiting for the app thread to complete.
int destroyRequested;
// -------------------------------------------------
// Below are "private" implementation of the glue code.
pthread_mutex_t mutex;
pthread_cond_t cond;
int msgread;
int msgwrite;
pthread_t thread;
struct android_poll_source cmdPollSource;
struct android_poll_source inputPollSource;
int running;
int stateSaved;
int destroyed;
int redrawNeeded;
AInputQueue* pendingInputQueue;
ANativeWindow* pendingWindow;
ARect pendingContentRect;
};
enum {
/**
* Looper data ID of commands coming from the app's main thread, which
* is returned as an identifier from ALooper_pollOnce(). The data for this
* identifier is a pointer to an android_poll_source structure.
* These can be retrieved and processed with android_app_read_cmd()
* and android_app_exec_cmd().
*/
LOOPER_ID_MAIN = 1,
/**
* Looper data ID of events coming from the AInputQueue of the
* application's window, which is returned as an identifier from
* ALooper_pollOnce(). The data for this identifier is a pointer to an
* android_poll_source structure. These can be read via the inputQueue
* object of android_app.
*/
LOOPER_ID_INPUT = 2,
/**
* Start of user-defined ALooper identifiers.
*/
LOOPER_ID_USER = 3,
};
enum {
/**
* Command from main thread: the AInputQueue has changed. Upon processing
* this command, android_app->inputQueue will be updated to the new queue
* (or NULL).
*/
APP_CMD_INPUT_CHANGED,
/**
* Command from main thread: a new ANativeWindow is ready for use. Upon
* receiving this command, android_app->window will contain the new window
* surface.
*/
APP_CMD_INIT_WINDOW,
/**
* Command from main thread: the existing ANativeWindow needs to be
* terminated. Upon receiving this command, android_app->window still
* contains the existing window; after calling android_app_exec_cmd
* it will be set to NULL.
*/
APP_CMD_TERM_WINDOW,
/**
* Command from main thread: the current ANativeWindow has been resized.
* Please redraw with its new size.
*/
APP_CMD_WINDOW_RESIZED,
/**
* Command from main thread: the system needs that the current ANativeWindow
* be redrawn. You should redraw the window before handing this to
* android_app_exec_cmd() in order to avoid transient drawing glitches.
*/
APP_CMD_WINDOW_REDRAW_NEEDED,
/**
* Command from main thread: the content area of the window has changed,
* such as from the soft input window being shown or hidden. You can
* find the new content rect in android_app::contentRect.
*/
APP_CMD_CONTENT_RECT_CHANGED,
/**
* Command from main thread: the app's activity window has gained
* input focus.
*/
APP_CMD_GAINED_FOCUS,
/**
* Command from main thread: the app's activity window has lost
* input focus.
*/
APP_CMD_LOST_FOCUS,
/**
* Command from main thread: the current device configuration has changed.
*/
APP_CMD_CONFIG_CHANGED,
/**
* Command from main thread: the system is running low on memory.
* Try to reduce your memory use.
*/
APP_CMD_LOW_MEMORY,
/**
* Command from main thread: the app's activity has been started.
*/
APP_CMD_START,
/**
* Command from main thread: the app's activity has been resumed.
*/
APP_CMD_RESUME,
/**
* Command from main thread: the app should generate a new saved state
* for itself, to restore from later if needed. If you have saved state,
* allocate it with malloc and place it in android_app.savedState with
* the size in android_app.savedStateSize. The will be freed for you
* later.
*/
APP_CMD_SAVE_STATE,
/**
* Command from main thread: the app's activity has been paused.
*/
APP_CMD_PAUSE,
/**
* Command from main thread: the app's activity has been stopped.
*/
APP_CMD_STOP,
/**
* Command from main thread: the app's activity is being destroyed,
* and waiting for the app thread to clean up and exit before proceeding.
*/
APP_CMD_DESTROY,
};
/**
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
* app command message.
*/
int8_t android_app_read_cmd(struct android_app* android_app);
/**
* Call with the command returned by android_app_read_cmd() to do the
* initial pre-processing of the given command. You can perform your own
* actions for the command after calling this function.
*/
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
/**
* Call with the command returned by android_app_read_cmd() to do the
* final post-processing of the given command. You must have done your own
* actions for the command before calling this function.
*/
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
/**
* No-op function that used to be used to prevent the linker from stripping app
* glue code. No longer necessary, since __attribute__((visibility("default")))
* does this for us.
*/
__attribute__((
deprecated("Calls to app_dummy are no longer necessary. See "
"https://github.com/android-ndk/ndk/issues/381."))) void
app_dummy();
/**
* This is the function that application code must implement, representing
* the main entry to the app.
*/
extern void android_main(struct android_app* app);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,115 @@
#include "animations.h"
#include "timer.h"
using namespace tp;
using namespace glw;
bool tp::glw::gInTransition = false;
halnf AnimValue::interpolate() const {
if (!mTimeAnim) {
return mVal;
}
auto dt = gCurrentTime - mTimeStart;
if (dt > mTimeAnim) dt = mTimeAnim;
auto t = (halnf)dt / mTimeAnim;
t = (halnf)(0.511 + atan((t - 0.36) * 28) / 2.9);
t = clamp(t, 0.f, 1.f);
auto out = mValPrev + (mVal - mValPrev) * t;
return out;
}
AnimValue::AnimValue() {}
void AnimValue::setAnimTime(halni time) {
mTimeAnim = time;
}
AnimValue::AnimValue(halnf val) {
mVal = val;
mValPrev = mVal;
mTimeStart = gCurrentTime;
}
bool AnimValue::inTransition() const {
auto time_pased = gCurrentTime - mTimeStart >= mTimeAnim;
return !time_pased;
}
void AnimValue::set(halnf val) {
if (!inTransition()) {
mValPrev = mVal;
}
if (val == mVal) {
return;
}
mValPrev = get();
mVal = val;
if (!inTransition()) {
mTimeStart = (halni)gCurrentTime;
}
}
halnf AnimValue::getTarget() const {
return mVal;
}
void AnimValue::setNoTransition(halnf val) {
mValPrev = val;
mVal = val;
mTimeStart -= mTimeStart;
}
halnf AnimValue::get() const {
if (inTransition()) {
gInTransition = true;
}
return interpolate();
}
AnimValue::operator halnf() const {
return get();
}
rectf AnimRect::get() const {
return { x.get(), y.get() , z.get(), w.get() };
}
rectf AnimRect::getTarget() const {
return { x.getTarget(), y.getTarget() , z.getTarget(), w.getTarget() };
}
void AnimRect::setNoTransition(rectf rec) {
x.setNoTransition(rec.x);
y.setNoTransition(rec.y);
z.setNoTransition(rec.z);
w.setNoTransition(rec.w);
}
void AnimRect::setAnimTime(const halni time) {
x.setAnimTime(time);
y.setAnimTime(time);
z.setAnimTime(time);
w.setAnimTime(time);
}
void AnimRect::set(const rectf& in) {
x.set(in.x);
y.set(in.y);
z.set(in.z);
w.set(in.w);
}
rgba AnimColor::get() const {
auto col = mColor.get();
return rgba(col.x, col.y, col.z, col.w);
}
void AnimColor::set(const rgba& col) {
mColor.set(rectf(col.r, col.g, col.b, col.a));
}

View file

@ -0,0 +1,378 @@
#include "canvas.h"
#include "GraphicsLibApi.h"
#ifdef ENV_OS_ANDROID
const char* get_android_abs_path(const char* path, bool second = false);
#define NANOVG_GLES3_IMPLEMENTATION // Use GLES3 implementation.
#else
#define NANOVG_GL3_IMPLEMENTATION // Use GL3 implementation.
#endif
#include "nanovg.h"
#include "nanovg_gl.h"
#include "nanovg_gl_utils.h"
using namespace tp;
using namespace glw;
#define NVG ((NVGcontext*) mCtx)
static const char* getFontPath() {
#ifdef ENV_OS_ANDROID
return get_android_abs_path("rsc/fonts/CONSOLAB.TTF");
#else
return "./rsc/fonts/CONSOLAB.TTF";
#endif
}
Canvas::Canvas() {
// create context
mCtx =
#ifdef ENV_OS_ANDROID
nvgCreateGLES3
#else
nvgCreateGL3
#endif
(NVG_ANTIALIAS | NVG_STENCIL_STROKES
#ifdef ENV_BUILD_DEBUG
| NVG_DEBUG
#endif
);
mColorPrimary = rgba(0.5f, 0.5f, 0.5f, 0.9f);
mColorSecondary = rgba(0.2f, 0.2f, 0.2f, 0.9f);
nvgCreateFont(NVG, "basic", getFontPath());
}
Canvas::Val& Canvas::convert2pxls(Val& val) {
val = val * mDpmm * mUIScale;
return val;
}
Canvas::Val Canvas::as2pxls(Val val) {
return val * mDpmm * mUIScale;
}
Canvas::Rect& Canvas::convert2pxls(Rect& rec) {
convert2pxls(rec.x);
convert2pxls(rec.y);
convert2pxls(rec.z);
convert2pxls(rec.w);
return rec;
}
Canvas::Vec2& Canvas::convert2pxls(Vec2& vec) {
convert2pxls(vec.x);
convert2pxls(vec.y);
return vec;
}
void Canvas::beginDraw(Rect viewport, tp::halnf dpmm, tp::halnf uiscale) {
convert2pxls(viewport);
nvgBeginFrame(NVG, viewport.size.x, viewport.size.y, 1);
mVieport = { 0.f, 0.f, viewport.size.x, viewport.size.y };
glViewport((GLsizei)mVieport.x, (GLsizei)mVieport.y, (GLsizei)mVieport.size.x, (GLsizei)mVieport.size.y);
mDpmm = dpmm;
mUIScale = tp::clamp(uiscale, 0.03f, 100.f);
mClamping = mVieport;
}
void Canvas::resetViewport() {
glViewport((GLsizei)mVieport.x, (GLsizei)mVieport.y, (GLsizei)mVieport.size.x, (GLsizei)mVieport.size.y);
}
void Canvas::clear(Col color) {
glClearColor(color.r, color.g, color.b, color.a);
//glClearDepth(0.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void Canvas::endDraw() {
nvgEndFrame(NVG);
}
void Canvas::setClamping(Rect rec) {
mClamping = rec;
convert2pxls(rec);
nvgScissor(NVG, rec.x, rec.y, rec.z, rec.w);
}
void Canvas::setCol1(const Col& col) {
mColorPrimary = col;
}
void Canvas::setCol2(const Col& col) {
mColorSecondary = col;
}
void Canvas::rect(Rect rec, halnf rounding, halnf borders) {
convert2pxls(rec);
convert2pxls(rounding);
convert2pxls(borders);
if (borders) {
nvgBeginPath(NVG);
auto borders_rec = rec;
rec.pos += borders;
rec.size -= borders * 2;
if (rounding) {
nvgRoundedRect(NVG, borders_rec.x, borders_rec.y, borders_rec.z, borders_rec.w, rounding);
}
else {
nvgRect(NVG, borders_rec.x, borders_rec.y, borders_rec.z, borders_rec.w);
}
nvgFillColor(NVG, nvgRGBAf(mColorSecondary.r, mColorSecondary.g, mColorSecondary.b, mColorSecondary.a));
nvgFill(NVG);
}
nvgBeginPath(NVG);
if (rounding) {
nvgRoundedRect(NVG, rec.x, rec.y, rec.z, rec.w, rounding);
}
else {
nvgRect(NVG, rec.x, rec.y, rec.z, rec.w);
}
nvgFillColor(NVG, nvgRGBAf(mColorPrimary.r, mColorPrimary.g, mColorPrimary.b, mColorPrimary.a));
nvgFill(NVG);
}
void Canvas::line(Vec2 p1, Vec2 p2, halnf thikness) {
convert2pxls(p1);
convert2pxls(p2);
convert2pxls(thikness);
nvgBeginPath(NVG);
nvgMoveTo(NVG, p1.x, p1.y);
nvgLineTo(NVG, p2.x, p2.y);
nvgStrokeColor(NVG, nvgRGBAf(mColorPrimary.r, mColorPrimary.g, mColorPrimary.b, mColorPrimary.a));
nvgStrokeWidth(NVG, thikness);
nvgStroke(NVG);
}
void Canvas::trig(Vec2 p1, Vec2 p2, Vec2 p3) {
convert2pxls(p1);
convert2pxls(p2);
convert2pxls(p3);
nvgBeginPath(NVG);
nvgMoveTo(NVG, p1.x, p1.y);
nvgLineTo(NVG, p2.x, p2.y);
nvgLineTo(NVG, p3.x, p3.y);
nvgFillColor(NVG, nvgRGBAf(mColorPrimary.r, mColorPrimary.g, mColorPrimary.b, mColorPrimary.a));
nvgFill(NVG);
}
void Canvas::circle(Vec2 rec, halnf radius, halnf borders) {
convert2pxls(rec);
convert2pxls(radius);
convert2pxls(borders);
DBG_BREAK(1);
}
void Canvas::text(const char* start, const char* end, Rect rec, halnf size, Align align) {
convert2pxls(rec);
convert2pxls(size);
nvgFillColor(NVG, nvgRGBAf(mColorPrimary.r, mColorPrimary.g, mColorPrimary.b, mColorPrimary.a));
nvgFontSize(NVG, size);
nvgTextAlign(NVG, NVGalign(align));
auto y = (align & Align::MIDDLE) ? rec.y + rec.w / 2 : rec.y;
auto x = (align & Align::CENTER) ? rec.x + rec.z / 2 : rec.x;
nvgText(NVG, x, y, start, end);
}
void Canvas::text(const string text, Rect rec, halnf size, Align align) {
convert2pxls(rec);
convert2pxls(size);
nvgFillColor(NVG, nvgRGBAf(mColorPrimary.r, mColorPrimary.g, mColorPrimary.b, mColorPrimary.a));
nvgFontSize(NVG, size);
nvgTextAlign(NVG, NVGalign(align));
//nvgTextBounds(NVG, );
auto t_mid = rec.y + rec.w / 2;
auto t_top = rec.y;
auto res = bool(align & Align::MIDDLE);
auto y = res ? t_mid : t_top;
auto x_mid = rec.x + rec.z / 2;
auto x_left = rec.x;
res = bool(align & Align::CENTER);
auto x = res ? x_mid : x_left;
nvgText(NVG, x, y, text.cstr(), text.cstr() + text.size());
}
void Canvas::ImageHandle::createFromBuff(glw::fbuffer* buff, Canvas* drawer) {
this->~ImageHandle();
auto const size = buff->getSize();
#ifdef ENV_OS_ANDROID
mId = nvglCreateImageFromHandleGLES3((NVGcontext*)drawer->mCtx, buff->texId(), size.x, size.y, 0);
#else
mId = nvglCreateImageFromHandleGL3((NVGcontext*)drawer->mCtx, buff->texId(), (halni) size.x, (halni) size.y, 0);
#endif
}
void Canvas::ImageHandle::free(Canvas* drawer) {
if (mId && (NVGcontext*)drawer->mCtx) {
nvgDeleteImage((NVGcontext*)drawer->mCtx, mId);
}
}
Canvas::ImageHandle::~ImageHandle() {}
void Canvas::drawImage(Rect rec, ImageHandle* image, halnf angle, halnf alpha, halnf rounding) {
convert2pxls(rec);
convert2pxls(rounding);
auto imgPaint = nvgImagePattern(NVG, rec.x, rec.y, rec.z, rec.w, angle, image->mId, alpha);
nvgBeginPath(NVG);
nvgRoundedRect(NVG, rec.x, rec.y, rec.z, rec.w, rounding);
nvgFillPaint(NVG, imgPaint);
nvgFill(NVG);
}
void Canvas::drawColorwheel(Rect rec, const rgb& col) {
convert2pxls(rec);
float const x = rec.x;
float const y = rec.y;
float const w = rec.z;
float const h = rec.w;
hsv hsv = tp::hsv(col);
float const hue = hsv.h / (NVG_PI * 2);
int i;
float r0, r1, ax, ay, bx, by, cx, cy, aeps, r;
NVGpaint paint;
nvgSave(NVG);
cx = x + w * 0.5f;
cy = y + h * 0.5f;
r1 = (w < h ? w : h) * 0.5f - as2pxls(1.0f);
r0 = r1 - as2pxls(3.0f);
aeps = 0.5f / r1; // half a pixel arc length in radians (2pi cancels out).
for (i = 0; i < 6; i++) {
float a0 = (float)i / 6.0f * NVG_PI * 2.0f - aeps;
float a1 = (float)(i + 1.0f) / 6.0f * NVG_PI * 2.0f + aeps;
nvgBeginPath(NVG);
nvgArc(NVG, cx, cy, r0, a0, a1, NVG_CW);
nvgArc(NVG, cx, cy, r1, a1, a0, NVG_CCW);
nvgClosePath(NVG);
ax = cx + cosf(a0) * (r0 + r1) * 0.5f;
ay = cy + sinf(a0) * (r0 + r1) * 0.5f;
bx = cx + cosf(a1) * (r0 + r1) * 0.5f;
by = cy + sinf(a1) * (r0 + r1) * 0.5f;
paint = nvgLinearGradient(NVG, ax, ay, bx, by, nvgHSLA(a0 / (NVG_PI * 2), 1.0f, 0.55f, 255), nvgHSLA(a1 / (NVG_PI * 2), 1.0f, 0.55f, 255));
nvgFillPaint(NVG, paint);
nvgFill(NVG);
}
nvgBeginPath(NVG);
nvgCircle(NVG, cx, cy, r0 - 0.5f);
nvgCircle(NVG, cx, cy, r1 + 0.5f);
nvgStrokeColor(NVG, nvgRGBA(0, 0, 0, 64));
nvgStrokeWidth(NVG, 1.0f);
nvgStroke(NVG);
// Selector
nvgSave(NVG);
nvgTranslate(NVG, cx, cy);
nvgRotate(NVG, hue * NVG_PI * 2);
// Marker on
nvgStrokeWidth(NVG, 2.0f);
nvgBeginPath(NVG);
nvgRect(NVG, r0 - 1, -3, r1 - r0 + 2, 6);
nvgStrokeColor(NVG, nvgRGBA(255, 255, 255, 192));
nvgStroke(NVG);
paint = nvgBoxGradient(NVG, r0 - 3, -5, r1 - r0 + 6, 10, 2, 4, nvgRGBA(0, 0, 0, 128), nvgRGBA(0, 0, 0, 0));
nvgBeginPath(NVG);
nvgRect(NVG, r0 - 2 - 10, -4 - 10, r1 - r0 + 4 + 20, 8 + 20);
nvgRect(NVG, r0 - 2, -4, r1 - r0 + 4, 8);
nvgPathWinding(NVG, NVG_HOLE);
nvgFillPaint(NVG, paint);
nvgFill(NVG);
// Center triangle
r = r0 - 6;
ax = cosf(120.0f / 180.0f * NVG_PI) * r;
ay = sinf(120.0f / 180.0f * NVG_PI) * r;
bx = cosf(-120.0f / 180.0f * NVG_PI) * r;
by = sinf(-120.0f / 180.0f * NVG_PI) * r;
nvgBeginPath(NVG);
nvgMoveTo(NVG, r, 0);
nvgLineTo(NVG, ax, ay);
nvgLineTo(NVG, bx, by);
nvgClosePath(NVG);
paint = nvgLinearGradient(NVG, r, 0, ax, ay, nvgHSLA(hue, 1.0f, 0.5f, 255), nvgRGBA(255, 255, 255, 255));
nvgFillPaint(NVG, paint);
nvgFill(NVG);
paint = nvgLinearGradient(NVG, (r + ax) * 0.5f, (0 + ay) * 0.5f, bx, by, nvgRGBA(0, 0, 0, 0), nvgRGBA(0, 0, 0, 255));
nvgFillPaint(NVG, paint);
nvgFill(NVG);
nvgStrokeColor(NVG, nvgRGBA(0, 0, 0, 64));
nvgStroke(NVG);
// Select circle on triangle
auto inner = as2pxls(3);
//auto outter = as2pxls(5);
float yt = hsv.v * hsv.s;
float xt = hsv.v - 0.5f * yt;
ay = sinf(120.0f / 180.0f * NVG_PI) * r * (-1.0f + xt * 2.0f);
ax = cosf(120.0f / 180.0f * NVG_PI) * r * (1.0f - yt * 3.f);
nvgStrokeWidth(NVG, as2pxls(0.6f));
nvgBeginPath(NVG);
nvgCircle(NVG, ax, ay, inner);
nvgStrokeColor(NVG, nvgRGBA(255, 255, 255, 192));
nvgStroke(NVG);
//paint = nvgRadialGradient(NVG, ax, ay, 4, 9, nvgRGBA(0, 0, 0, 64), nvgRGBA(0, 0, 0, 0));
//nvgBeginPath(NVG);
//nvgRect(NVG, ax - outter, ay - outter, outter * 2, outter * 2);
//nvgCircle(NVG, ax, ay, outter);
//nvgPathWinding(NVG, NVG_HOLE);
//nvgFillPaint(NVG, paint);
//nvgFill(NVG);
nvgRestore(NVG);
nvgRestore(NVG);
}
Canvas::~Canvas() {
if (NVG) {
#ifdef ENV_OS_ANDROID
nvgDeleteGLES3(NVG);
#else
nvgDeleteGL3(NVG);
#endif
}
mCtx = NULL;
}

View file

@ -0,0 +1,877 @@
#include "debugui.h"
#include "typelist.h"
#include "stringt.h"
#include "stack.h"
#include "npple.h"
#include "rect.h"
#include "map.h"
#include "imgui.h"
#include "imgui_internal.h"
#include "implot.h"
// -------------- Platform Includes ---------------------- //
#if defined ENV_OS_WINDOWS
#include "backends/imgui_impl_opengl3.h"
#include "backends/imgui_impl_win32.h"
#include <Windows.h>
#elif defined ENV_OS_LINUX
#include "backends/imgui_impl_opengl3.h"
#include "backends/imgui_impl_glfw.h"
#elif defined ENV_OS_ANDROID
#include "backends/imgui_impl_opengl3.h"
#include "backends/imgui_impl_android.h"
#include <android/sensor.h>
#include <android/log.h>
#include <android/input.h>
#include <string.h>
#endif
#if defined ENV_OS_WINDOWS
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
extern HWND windowsPlatformContextGetNativeWindow(tp::glw::PlatformContext* ctx);
extern UINT windowsPlatformContextGetMessage(tp::glw::PlatformContext* ctx);
extern WPARAM windowsPlatformContextGetWparam(tp::glw::PlatformContext* ctx);
extern LRESULT windowsPlatformContextGetLparam(tp::glw::PlatformContext* ctx);
#elif defined ENV_OS_LINUX
#elif defined ENV_OS_ANDROID
extern AInputEvent* androidPlatformContextGetEvent(tp::glw::PlatformContext* ctx);
extern const char* get_android_abs_path(const char* path, bool second = false);
const char* AndroidGetImGuiIniPath() {
static char imgui_path[512];
auto tmp = get_android_abs_path("imgui.ini");
memcpy(imgui_path, tmp, strlen(tmp));
return imgui_path;
}
#endif
static const char* getFontPath() {
#ifdef ENV_OS_ANDROID
return get_android_abs_path("rsc/fonts/CONSOLAB.TTF");
#else
return "./rsc/fonts/CONSOLAB.TTF";
#endif
}
namespace tp {
namespace glw {
struct DebugUiInternalContext {
DebugUiDrawCallBack* cb = NULL;
void* cd = NULL;
ImGuiContext* ui_context = NULL;
ImGuiContext* vk_context = NULL;
bool initialized = false;
bool ui_first_drawn = false;
bool vk_first_drawn = false;
bool ui_context_want_active = true;
bool ui_context_want_active_vk = false;
bool vk_context_want_active = false;
tp::halnf dpmm;
tp::halnf font_size_mm;
tp::halnf ui_scale;
struct VKState {
struct InputButton {
char val = '?';
char shift_val = '?';
tp::halnf width = 1;
void (*action)(InputButton* button, VKState* state) = [](InputButton* button, VKState* state) {
if (state->mShifted) {
ImGui::GetIO().AddInputCharacter(button->shift_val);
}
else {
ImGui::GetIO().AddInputCharacter(button->val);
}
};
const char* display_name = NULL;
};
struct Config {
InputButton** rows = NULL;
tp::halni nrows = NULL;
tp::halnf* rows_widths = NULL;
tp::halnf* rows_sizes = NULL;
};
bool mShifted = false;
static const tp::uhalni mNConfigs = 2;
Config mConfigs[mNConfigs];
tp::uhalni mActiveConfigIdx = 0;
InputButton mCommonRow[7];
//bool mActive = false;
VKState() {
mCommonRow[0] = { '^', '^', 1 , [](InputButton* button, VKState* state) { state->mShifted = !state->mShifted; } };
mCommonRow[1] = { ' ', ' ', 1,
[](InputButton* button, VKState* state) {
state->mActiveConfigIdx++;
if (state->mActiveConfigIdx == state->mNConfigs) {
state->mActiveConfigIdx = 0;
}
},
"..."
};
mCommonRow[2] = { ' ', ' ', 2 };
mCommonRow[3] = { '<', '<', 2, [](InputButton* button, VKState* state) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_Backspace, true);
ImGui::GetIO().AddKeyEvent(ImGuiKey_Backspace, false);
} };
mCommonRow[4] = { '.', '.', 1 };
mCommonRow[5] = { '@', '@', 1 };
mCommonRow[6] = { ' ', ' ', 2, [](InputButton* button, VKState* state) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_Enter, true);
ImGui::GetIO().AddKeyEvent(ImGuiKey_Enter, false);
}, "Entrer" };
setupMainConfig(mConfigs[0]);
setupAdditionalConfig(mConfigs[1]);
}
void setupMainConfig(Config& config) {
static InputButton row1[] = {
{'1', '!'},
{'2', '@'},
{'3', '#'},
{'4', '$'},
{'5', '%'},
{'6', '^'},
{'7', '&'},
{'8', '*'},
{'9', '('},
{'0', ')'},
};
static InputButton row2[] = {
{'q', 'Q'},
{'w', 'W'},
{'e', 'E'},
{'r', 'R'},
{'t', 'T'},
{'y', 'Y'},
{'u', 'U'},
{'i', 'I'},
{'o', 'O'},
{'p', 'P'},
};
static InputButton row3[] = {
{'a', 'A'},
{'s', 'S'},
{'d', 'D'},
{'f', 'F'},
{'g', 'G'},
{'h', 'H'},
{'j', 'J'},
{'k', 'K'},
{'l', 'L'},
};
static InputButton row4[] = {
{'z', 'Z'},
{'x', 'X'},
{'c', 'C'},
{'v', 'V'},
{'b', 'B'},
{'n', 'N'},
{'m', 'M'},
};
static InputButton* rows[] = {
row1,
row2,
row3,
row4,
mCommonRow,
};
static tp::halnf rows_sizes[IM_ARRAYSIZE(rows)] = {
IM_ARRAYSIZE(row1),
IM_ARRAYSIZE(row2),
IM_ARRAYSIZE(row3),
IM_ARRAYSIZE(row4),
IM_ARRAYSIZE(mCommonRow),
};
static tp::halnf rows_widths[IM_ARRAYSIZE(rows)];
for (auto row = 0; row < IM_ARRAYSIZE(rows); row++) {
for (auto butt = 0; butt < rows_sizes[row]; butt++) {
rows_widths[row] += rows[row][butt].width;
}
}
config = {
rows,
IM_ARRAYSIZE(rows),
rows_widths,
rows_sizes,
};
}
void setupAdditionalConfig(Config& config) {
static InputButton row1[] = {
{'~', '~'},
{'`', '`'},
{'_', '_'},
{'|', '|'},
{'\\', '\\'},
{'/', '/'},
{'"', '"'},
{'\'', '\''},
};
static InputButton row2[] = {
{';', ';'},
{':', ':'},
{'?', '?'},
{',', ','},
{'[', '['},
{']', ']'},
{'{', '{'},
{'}', '}'},
{'(', '('},
{')', ')'},
};
static InputButton row3[] = {
{'-', '-'},
{'=', '='},
{'*', '*'},
{'+', '+'},
{'<', '<'},
{'>', '>'},
};
static InputButton row4[] = {
{' ', ' ', 1, [](InputButton* butt, VKState* state) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_UpArrow, true);
ImGui::GetIO().AddKeyEvent(ImGuiKey_UpArrow, false);
}, "up" },
{' ', ' ', 1, [](InputButton* butt, VKState* state) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_DownArrow, true);
ImGui::GetIO().AddKeyEvent(ImGuiKey_DownArrow, false);
}, "down"},
{' ', ' ', 1, [](InputButton* butt, VKState* state) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_LeftArrow, true);
ImGui::GetIO().AddKeyEvent(ImGuiKey_LeftArrow, false);
}, "left"},
{' ', ' ', 1, [](InputButton* butt, VKState* state) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_RightArrow, true);
ImGui::GetIO().AddKeyEvent(ImGuiKey_RightArrow, false);
}, "right"},
};
static InputButton* rows[] = {
row1,
row2,
row3,
row4,
mCommonRow,
};
static tp::halnf rows_sizes[IM_ARRAYSIZE(rows)] = {
IM_ARRAYSIZE(row1),
IM_ARRAYSIZE(row2),
IM_ARRAYSIZE(row3),
IM_ARRAYSIZE(row4),
IM_ARRAYSIZE(mCommonRow),
};
static tp::halnf rows_widths[IM_ARRAYSIZE(rows)];
for (auto row = 0; row < IM_ARRAYSIZE(rows); row++) {
for (auto butt = 0; butt < rows_sizes[row]; butt++) {
rows_widths[row] += rows[row][butt].width;
}
}
config = {
rows,
IM_ARRAYSIZE(rows),
rows_widths,
rows_sizes,
};
}
} mVKState;
DebugUiInternalContext(Window& window, DebugUiDrawCallBack* acb, void* acd) {
cb = acb;
cd = acd;
ui_context = ImGui::CreateContext();
ImGui::SetCurrentContext(ui_context);
ImGuiContextInit(window);
vk_context = ImGui::CreateContext();
ImGui::SetCurrentContext(vk_context);
ImGuiContextInit(window);
initialized = true;
}
void drawDebugUI(tp::halnf a_dpmm, tp::halnf a_font_size_mm, tp::halnf a_ui_scale) {
dpmm = a_dpmm;
font_size_mm = a_font_size_mm;
ui_scale = a_ui_scale;
if (!initialized) return;
ImGui::SetCurrentContext(ui_context);
ImGui::ApplyStyle(dpmm, font_size_mm, ui_scale);
ImGuiContextBeginFrame();
cb(cd);
ImGuiContextEndFrame();
ImGui::SetCurrentContext(NULL);
ui_first_drawn = true;
if (ui_context_want_active_vk || vk_context_want_active) {
ImGui::SetCurrentContext(vk_context);
ImGui::ApplyStyle(dpmm, font_size_mm, ui_scale);
ImGuiContextBeginFrame();
VirtualKeyboard(dpmm, font_size_mm, ui_scale);
ImGuiContextEndFrame();
ImGui::SetCurrentContext(NULL);
vk_first_drawn = true;
}
}
~DebugUiInternalContext() {
if (!initialized) return;
ImGui::SetCurrentContext(ui_context);
ImGuiContextDeinit();
ImGui::DestroyContext(ui_context);
ImGui::SetCurrentContext(vk_context);
ImGuiContextDeinit();
ImGui::DestroyContext(vk_context);
}
private:
void ImGuiContextInit(Window& window) {
#if defined ENV_OS_WINDOWS
ImGui_ImplOpenGL3_Init();
ImGui_ImplWin32_Init(window.platform_window());
window.mNativeEventListeners.put("imgui", {
nativeWindowEventListenerWrapBegin,
nativeWindowEventListenerWrap,
nativeWindowEventListenerWrapEnd,
this
});
#elif defined ENV_OS_LINUX
ImGui_ImplOpenGL3_Init();
ImGui_ImplGlfw_InitForOpenGL((GLFWwindow*)window.platform_window(), true);
#elif defined ENV_OS_ANDROID
ImGui_ImplOpenGL3_Init("#version 300 es");
ImGui_ImplAndroid_Init((ANativeWindow*)window.platform_window());
ImGui::GetIO().IniFilename = AndroidGetImGuiIniPath();
window.mNativeEventListeners.put("imgui", {
nativeWindowEventListenerWrapBegin,
nativeWindowEventListenerWrap,
nativeWindowEventListenerWrapEnd,
this
});
#endif
}
void ImGuiContextBeginFrame() {
#if defined ENV_OS_WINDOWS
ImGui_ImplWin32_NewFrame();
ImGui_ImplOpenGL3_NewFrame();
#elif defined ENV_OS_LINUX
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
#elif defined ENV_OS_ANDROID
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplAndroid_NewFrame();
#endif
ImGui::NewFrame();
}
void ImGuiContextEndFrame() {
ImGui::Render();
#if defined ENV_OS_WINDOWS
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
#elif defined ENV_OS_LINUX
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
#elif defined ENV_OS_ANDROID
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
#endif
}
void ImGuiContextDeinit() {
#if defined ENV_OS_WINDOWS
ImGui_ImplWin32_Shutdown();
#elif defined ENV_OS_LINUX
ImGui_ImplGlfw_Shutdown();
#elif defined ENV_OS_ANDROID
ImGui_ImplAndroid_Shutdown();
#endif
ImGui_ImplOpenGL3_Shutdown();
}
void nativeWindowEventListenerPlatform(tp::glw::PlatformContext* ctx) {
#if defined ENV_OS_WINDOWS
auto native_window = windowsPlatformContextGetNativeWindow(ctx);
auto message = windowsPlatformContextGetMessage(ctx);
auto w_param = windowsPlatformContextGetWparam(ctx);
auto l_param = windowsPlatformContextGetLparam(ctx);
// IMGUI : needs to be commented out
//if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr)
// ::SetCapture(hwnd);
ImGui_ImplWin32_WndProcHandler(native_window, message, w_param, l_param);
#elif defined ENV_OS_LINUX
#elif defined ENV_OS_ANDROID
auto aev = androidPlatformContextGetEvent(ctx);
int32_t event_type = AInputEvent_getType(aev);
ImGui_ImplAndroid_HandleInputEvent(aev);
auto& io = ImGui::GetIO();
if (event_type == AINPUT_EVENT_TYPE_MOTION); {
int32_t event_action = AMotionEvent_getAction(aev);
int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
event_action &= AMOTION_EVENT_ACTION_MASK;
if ((AMotionEvent_getToolType(aev, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER)
|| (AMotionEvent_getToolType(aev, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) {
if (event_action == AMOTION_EVENT_ACTION_DOWN) {
io.AddMousePosEvent(AMotionEvent_getX(aev, event_pointer_index), AMotionEvent_getY(aev, event_pointer_index));
}
else if (event_action == AMOTION_EVENT_ACTION_UP) {
io.AddMousePosEvent(AMotionEvent_getX(aev, event_pointer_index), AMotionEvent_getY(aev, event_pointer_index));
if (!ui_context_want_active_vk || vk_context_want_active) {
io.AddMousePosEvent(-1, -1);
}
}
}
}
#endif
}
void nativeWindowEventListenerBegin(tp::glw::PlatformContext* ctx) {
vk_context_want_active = vk_context->IO.WantCaptureMouse || vk_context->IO.WantSetMousePos;
ui_context_want_active_vk = ui_context->IO.WantTextInput;
ui_context_want_active = ui_context->IO.WantCaptureKeyboard || ui_context->IO.WantCaptureMouse || ui_context->IO.WantSetMousePos;
}
void nativeWindowEventListener(tp::glw::PlatformContext* ctx) {
// proc vk
if ((ui_context_want_active_vk || vk_context_want_active) && vk_first_drawn) {
ImGui::SetCurrentContext(vk_context);
nativeWindowEventListenerPlatform(ctx);
while (vk_context->InputEventsQueue.Size) {
ImGui::ApplyStyle(dpmm, font_size_mm, ui_scale);
ImGui::NewFrame();
VirtualKeyboard(dpmm, font_size_mm, ui_scale);
ImGui::Render();
vk_context_want_active |= vk_context->IO.WantCaptureMouse || vk_context->IO.WantSetMousePos;
}
vk_context_want_active |= vk_context->IO.WantCaptureMouse || vk_context->IO.WantSetMousePos;
ImGui::SetCurrentContext(NULL);
}
// proc ui
if (!vk_context_want_active && ui_first_drawn) {
ImGui::SetCurrentContext(ui_context);
nativeWindowEventListenerPlatform(ctx);
while (ui_context->InputEventsQueue.Size) {
ImGui::ApplyStyle(dpmm, font_size_mm, ui_scale);
ImGui::NewFrame();
cb(cd);
ImGui::Render();
ui_context_want_active_vk |= ui_context->IO.WantTextInput;
ui_context_want_active |= ui_context->IO.WantCaptureKeyboard || ui_context->IO.WantCaptureMouse || ui_context->IO.WantSetMousePos;
}
ImGui::SetCurrentContext(NULL);
}
}
void nativeWindowEventListenerEnd(tp::glw::PlatformContext* ctx) {}
static void nativeWindowEventListenerWrapBegin(tp::glw::PlatformContext* ctx, void* cd) { ((DebugUiInternalContext*)cd)->nativeWindowEventListenerBegin(ctx); }
static void nativeWindowEventListenerWrap(tp::glw::PlatformContext* ctx, void* cd) { ((DebugUiInternalContext*)cd)->nativeWindowEventListener(ctx); }
static void nativeWindowEventListenerWrapEnd(tp::glw::PlatformContext* ctx, void* cd) { ((DebugUiInternalContext*)cd)->nativeWindowEventListenerEnd(ctx); }
void VirtualKeyboard(tp::halnf dpmm, tp::halnf font_size_mm, tp::halnf ui_scale) {
auto& config = mVKState.mConfigs[mVKState.mActiveConfigIdx];
ImGuiID dockspace_id = ImGui::DockSpaceOverViewport(0, ImGuiDockNodeFlags_PassthruCentralNode);
ImGuiDockNode* node = ImGui::DockContextFindNodeByID(ImGui::GetCurrentContext(), dockspace_id);
if (!node->IsSplitNode()) {
node->MergedFlags |= ImGuiDockNodeFlags_HiddenTabBar;
ImGuiID dock_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Down, 0.33f, nullptr, &dockspace_id);
ImGui::DockBuilderDockWindow("VK", dock_id);
}
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0.f, 0.f });
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, { 50 * dpmm, 30 * dpmm });
ImGui::Begin("VK");
ImGui::PopStyleVar(3);
auto pos = ImGui::GetCursorPos();
auto width = ImGui::GetWindowWidth();
auto height = ImGui::GetWindowHeight() - pos.y;
//auto scale = dpmm * ui_scale;
auto padding = dpmm * 2 * ui_scale;
auto button_sizey = (tp::halnf)(height - (config.nrows + 1) * padding) / config.nrows;
tp::vec2f crs = { pos.x + padding, pos.y + padding };
for (auto row = 0; row < config.nrows; row++) {
tp::halnf butt_scalex = (width - (config.rows_sizes[row] + 1) * padding) / config.rows_widths[row];
crs.x = pos.x + padding;
for (auto butt = 0; butt < config.rows_sizes[row]; butt++) {
auto& button = config.rows[row][butt];
ImGui::SetCursorPos({ crs.x, crs.y });
char name[2] = { mVKState.mShifted ? button.shift_val : button.val, 0 };
ImGui::PushID(&button);
if (!ImGui::Button(button.display_name ? button.display_name: name, { butt_scalex * button.width, button_sizey })) {
crs.x += butt_scalex * button.width + padding;
ImGui::PopID();
continue;
}
ImGui::SetCurrentContext(ui_context);
button.action(&button, &mVKState);
ImGui::SetCurrentContext(vk_context);
ImGui::PopID();
}
crs.y += button_sizey + padding;
}
ImGui::End();
}
};
}
}
tp::glw::DebugUI::DebugUI(Window& window, DebugUiDrawCallBack* acb, void* acd) {
mDPMM = window.mDevice.mDPMM;
// map device size to font and ui size
{
auto device_max = 600.f;
auto device_min = 50.f;
auto device = tp::clamp(window.mDevice.Size.x / mDPMM, device_min, device_max);
auto device_factor = (device - device_min) / (device_max - device_min);
auto font_min = 2.f;
auto font_max = 3.5f;
mFontSizeMM = (font_max - font_min) * device_factor + font_min;
auto ui_max = 1.f;
auto ui_min = 0.45f;
mUIScale = (ui_max - ui_min) * device_factor + ui_min;
}
mCtx = new DebugUiInternalContext(window, acb, acd);
}
void tp::glw::DebugUI::drawDebugUI(tp::halnf dpmm) {
mDPMM = dpmm;
mCtx->drawDebugUI(mDPMM, mFontSizeMM, mUIScale);
}
tp::glw::DebugUI::~DebugUI() {
delete mCtx;
}
// ------------------------------------------------------ //
static const int sWindowFrameFlags = (ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoDecoration);
bool ImGui::SubMenuBegin(const char* desc, int level) {
if (level == 1) {
ImGui::Separator();
}
if (ImGui::TreeNode(desc)) {
GImGui->Style.IndentSpacing = 6;
return true;
}
return false;
}
void ImGui::SubMenuEnd(int level) {
ImGui::TreePop();
}
void ImGui::ToolTip(const char* desc) {
ImGui::SameLine();
ImGui::TextDisabled("*");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(desc);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
ImGui::PopupData ImGui::HoverPopupBeginButton(const char* id, tp::vec2f butsize, tp::vec2f popupsize) {
ImVec2 curs = ImGui::GetCursorScreenPos();
tp::vec2f popup_pos = tp::vec2f(curs.x + butsize.x / 2.f - popupsize.x / 2.f, (curs.y + butsize.y + 7));
ImGui::Button(id, ImVec2(butsize.x, butsize.y)); ImGui::SameLine();
return HoverPopupBegin(id, popupsize, popup_pos);
}
ImGui::PopupData ImGui::HoverPopupBegin(const char* str_id, tp::vec2f size, tp::vec2f pos_p, ImGuiPopupFlags popup_flags) {
ImGui::PopupData out;
out.ishovered = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
if (out.ishovered) {
ImVec2 pos;
if (pos_p == tp::vec2f(-1)) {
pos = GImGui->CurrentWindow->DC.CursorPos;
}
else {
pos.x = pos_p.x;
pos.y = pos_p.y;
}
ImGui::SetNextWindowPos(pos);
out.p1 = { pos.x, pos.y };
ImGui::OpenPopup(str_id);
out.p2 = out.p1;
out.p2.x += ImGui::GetWindowWidth();
}
if (BeginPopup(str_id, ImGuiWindowFlags_NoMove)) {
out.opened = true;
auto pos = GetWindowPos();
out.p1 = { pos.x, pos.y };
out.p2 = out.p1;
out.p2.x += ImGui::GetWindowWidth();
}
return out;
}
void ImGui::HoverPopupEnd(ImGui::PopupData& in) {
if (!in.opened) {
return;
}
in.ishovered |= IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ChildWindows);
tp::vec2f mousepos = { ImGui::GetMousePos().x, ImGui::GetMousePos().y };
tp::halnf tollerance = 10;
tp::rectf tollerace_rect = tp::rectf(tp::vec2f(in.p1.x, in.p1.y - tollerance), tp::vec2f(in.p2.x - in.p1.x, tollerance * 2.f));
bool is_tollerance = tollerace_rect.inside(mousepos);
if (!(in.ishovered || is_tollerance)) {
CloseCurrentPopup();
}
EndPopup();
}
#define VAL(val) (val * dpmm / 3 * ui_scale)
#define VEC(x, y) ImVec2(x * dpmm / 3, y * dpmm / 3 * ui_scale)
void ImGui::ApplyStyle(tp::halnf dpmm, float font_size_mm, float ui_scale) {
ImGuiStyle* style = &ImGui::GetStyle();
auto& io = ImGui::GetIO();
bool first_init = io.Fonts->Fonts.Size == 0;
auto font_path = getFontPath();
// supports PDMM from 0.3 to 10
const float min_dpmm = 0.3f;
const float max_dpmm = 20.f;
dpmm = tp::clamp(dpmm, min_dpmm, max_dpmm);
// calc font
const float font_size_mm_max = 10;
const float font_size_pixels_min = 9;
float font_size_mm_min = font_size_pixels_min / dpmm;
const int font_quality_steps = 3;
font_size_mm = tp::clamp(font_size_mm, font_size_mm_min, font_size_mm_max);
float font_sizes[font_quality_steps];
for (auto i = 0; i < font_quality_steps; i++) {
auto mm = font_size_mm_min + (font_size_mm_max - font_size_mm_min) * tp::pow(((tp::halnf) (i + 1) / font_quality_steps), 3);
font_sizes[i] = dpmm * mm;
}
if (first_init) {
for (auto i = 0; i < font_quality_steps; i++) {
io.Fonts->AddFontFromFileTTF(font_path, font_sizes[i] * 1);
}
}
// select fonts
auto const pixels_required = dpmm * font_size_mm;
auto idx = -1;
for (auto i = 0; i < font_quality_steps; i++) {
idx = i;
if (pixels_required <= font_sizes[i]) {
break;
}
}
DBG_BREAK(idx == -1);
io.FontDefault = io.Fonts->Fonts[idx];
io.FontGlobalScale = font_size_mm / (font_size_mm_min + ((font_size_mm_max - font_size_mm_min) / font_quality_steps) * (idx + 1));
io.FontGlobalScale = pixels_required / font_sizes[idx];
auto rounding = VAL(5);
auto pudding = VEC(7, 7);
style->WindowPadding = pudding;
style->WindowRounding = rounding;
//style->WindowMinSize = VEC(11, 11);
style->ChildRounding = rounding;
style->PopupRounding = rounding;
style->FramePadding = pudding;
style->FrameRounding = rounding;
style->ItemSpacing = VEC(4, 11);
style->ItemInnerSpacing = VEC(9, 4);
style->CellPadding = pudding;
style->TouchExtraPadding = pudding;
style->IndentSpacing = VAL(25);
style->ColumnsMinSpacing = VAL(5);
style->ScrollbarSize = VAL(16);
style->ScrollbarRounding = rounding;
style->GrabMinSize = VAL(2);
style->GrabRounding = rounding;
style->LogSliderDeadzone = VAL(2);
style->TabRounding = rounding;
style->TabMinWidthForCloseButton = VAL(5);
style->DisplayWindowPadding = pudding;
style->DisplaySafeAreaPadding = pudding;
style->MouseCursorScale = VAL(5);
style->FrameBorderSize = VAL(0);
style->WindowBorderSize = VAL(1.5f);
style->ChildBorderSize = VAL(2);
style->WindowTitleAlign = VEC(0.5f, 0.6f);
style->WindowMenuButtonPosition = ImGuiDir_Right;
if (!first_init)
return;
ImVec4* colors = ImGui::GetStyle().Colors;
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.09f, 0.09f, 0.10f, 1.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.10f, 1.00f);
colors[ImGuiCol_Border] = ImVec4(0.06f, 0.06f, 0.06f, 1.00f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.14f, 0.14f, 0.17f, 1.00f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.27f, 0.27f, 0.33f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.43f, 0.43f, 0.53f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.09f, 0.09f, 0.10f, 0.00f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.15f, 0.15f, 0.19f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.21f, 0.20f, 0.25f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.30f, 0.29f, 0.35f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.48f, 0.50f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.53f, 0.57f, 0.64f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.69f, 0.74f, 0.83f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.14f, 0.14f, 0.17f, 1.00f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.27f, 0.27f, 0.33f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.43f, 0.43f, 0.53f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.14f, 0.14f, 0.17f, 1.00f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.27f, 0.27f, 0.33f, 1.00f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.33f, 0.33f, 0.40f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.37f, 0.37f, 0.40f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.33f, 0.35f, 0.38f, 1.00f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.37f, 0.39f, 0.42f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.16f, 0.17f, 0.19f, 1.00f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.33f, 0.35f, 0.38f, 1.00f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.28f, 0.34f, 0.42f, 1.00f);
colors[ImGuiCol_Tab] = ImVec4(0.14f, 0.14f, 0.17f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.23f, 0.23f, 0.28f, 1.00f);
colors[ImGuiCol_TabActive] = ImVec4(0.23f, 0.23f, 0.28f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.43f, 0.45f, 0.48f, 1.00f);
colors[ImGuiCol_DockingPreview] = ImVec4(0.81f, 0.81f, 0.81f, 0.18f);
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.33f, 0.35f, 0.38f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(0.49f, 0.51f, 0.54f, 1.00f);
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.13f, 0.13f, 0.20f, 1.00f);
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
colors[ImGuiCol_TableRowBg] = ImVec4(0.23f, 0.20f, 0.20f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.38f, 0.22f, 0.22f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.10f, 1.00f);
colors[ImGuiCol_DockingPreview] = ImVec4(0.64f, 0.75f, 0.83f, 0.18f);
colors[ImGuiCol_TabHovered] = ImVec4(0.39f, 0.39f, 0.47f, 1.00f);
colors[ImGuiCol_TabActive] = ImVec4(0.27f, 0.27f, 0.33f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.14f, 0.14f, 0.17f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.24f, 0.24f, 0.29f, 1.00f);
colors[ImGuiCol_Border] = ImVec4(0.65f, 0.65f, 0.65f, 0.70f);
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
}

View file

@ -0,0 +1,116 @@
#include "fbuffer.h"
#include "GraphicsLibApi.h"
void glerr(GLenum type);
#define AssertGL(x) { x; GLenum __gle = glGetError(); if (__gle != GL_NO_ERROR) glerr(__gle); }
namespace tp {
using namespace glw;
fbuffer::fbuffer(const vec2f& size) : mSize(size) {
mDrawBuffers[0] = {GL_COLOR_ATTACHMENT0};
// --------- texture ---------
AssertGL(glGenTextures(1, &mTextureId));
AssertGL(glBindTexture(GL_TEXTURE_2D, mTextureId));
// Give an empty image to OpenGL ( the last "0" )
AssertGL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)size.x, (GLsizei)size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
// Poor filtering. Needed
AssertGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
AssertGL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
// --------- depth ---------
AssertGL(glGenRenderbuffers(1, &mDepthBufferID));
AssertGL(glBindRenderbuffer(GL_RENDERBUFFER, mDepthBufferID));
AssertGL(glRenderbufferStorage(GL_RENDERBUFFER, GLW_CONTEXT_DEPTH_COMPONENT, (GLsizei)size.x, (GLsizei)size.y));
// ------------ framebuffer ------------
AssertGL(glGenFramebuffers(1, &mFrameBufferID));
AssertGL(glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferID));
AssertGL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthBufferID));
// Set "renderedTexture" as our colour attachement #0
AssertGL(glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTextureId, 0));
// Set the list of draw buffers.
AssertGL(glDrawBuffers(1, mDrawBuffers)); // "1" is the size of DrawBuffers
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
fbuffer::fbuffer(const vec2f& size, tp::uint1 samples) : mSize(size) {
mDrawBuffers[0] = { GL_COLOR_ATTACHMENT0 };
// ------- texture ---------
AssertGL(glGenTextures(1, &mTextureId));
AssertGL(glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, mTextureId));
#ifdef ENV_OS_ANDROID
AssertGL(glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGBA, (GLsizei)size.x, (GLsizei)size.y, GL_TRUE));
#else
AssertGL(glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGBA, (GLsizei)size.x, (GLsizei)size.y, GL_TRUE));
#endif
// !?
//AssertGL(glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
//AssertGL(glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
// ------- depth -------
AssertGL(glGenRenderbuffers(1, &mDepthBufferID));
AssertGL(glBindRenderbuffer(GL_RENDERBUFFER, mDepthBufferID));
AssertGL(glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GLW_CONTEXT_DEPTH_COMPONENT, (GLsizei)size.x, (GLsizei)size.y));
// ------- fbuff -------
AssertGL(glGenFramebuffers(1, &mFrameBufferID));
AssertGL(glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferID));
AssertGL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, mTextureId, 0));
AssertGL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthBufferID));
AssertGL(glDrawBuffers(1, mDrawBuffers));
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
tp::uint4 fbuffer::buffId() const {
return mFrameBufferID;
}
void fbuffer::beginDraw() {
AssertGL(glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferID));
setViewport({ 0.f, 0.f, mSize.x, mSize.y });
}
void fbuffer::setViewport(const rectf& viewport) {
AssertGL(glViewport((GLsizei)viewport.x, (GLsizei)viewport.y, (GLsizei)viewport.z, (GLsizei)viewport.w));
}
void fbuffer::clear() {
AssertGL(glClearColor(mClearCol.r, mClearCol.g, mClearCol.b, mClearCol.a));
AssertGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
}
void fbuffer::endDraw() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0, 0, 0, 0);
glUseProgram(0);
}
fbuffer::~fbuffer() {
glDeleteFramebuffers(1, &mFrameBufferID);
glDeleteTextures(1, &mTextureId);
glDeleteRenderbuffers(1, &mDepthBufferID);
}
uhalni glw::fbuffer::sizeAllocatedMem() const {
return uhalni(sizeof(GLenum) * 4 + mSize.sizeAllocatedMem() + (sizeof(mClearCol) + 1) * (alni) mSize.x * (alni) mSize.y);
}
uhalni glw::fbuffer::sizeUsedMem() const {
return sizeAllocatedMem();
}
uint4 glw::fbuffer::texId() const {
return mTextureId;
}
const vec2f& fbuffer::getSize() const { return mSize; }
};

View file

@ -0,0 +1,282 @@
#include "window.h"
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include <iostream>
#include <stdlib.h>
tp::glw::Window::Device tp::glw::Window::mDevice;
static void glfw_error_callback(int error, const char* description) {
std::cout << stderr << "Glfw Error" << error << " " << description << "\n";
}
void tp::glw::Window::init() {
tp::glw::Window::mDevice.Size;
tp::glw::Window::mDevice.FrameRate;
// Setup window
setenv("DISPLAY", ":0.0", 0); // does overwrite
glfwSetErrorCallback(glfw_error_callback);
RelAssert(glfwInit());
// GL 3.0 + GLSL 130
const char* glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
GLFWmonitor* primary = glfwGetPrimaryMonitor();
auto mode = glfwGetVideoMode(primary);
tp::glw::Window::mDevice.Size = { mode->width, mode->height };
tp::glw::Window::mDevice.FrameRate = mode->refreshRate;
}
#define MAP_EV(SYSKEY, code) case SYSKEY: { self->mEvents.mKeysQueue.push(tp::KeyEvent{ code, state }); break; }
namespace tp {
namespace glw {
struct PlatformContext {
GLFWwindow* window;
tp::glw::Window* window_ctx = NULL;
PlatformContext(tp::glw::Window* a_window_ctx) {
window_ctx = a_window_ctx;
// Create window with graphics context
window = glfwCreateWindow(300, 300, " ", NULL, NULL);
RelAssert(window);
glfwMakeContextCurrent(window);
GLenum err = glewInit();
if (GLEW_OK != err)
{
std::cout << stderr << "Error:" << glewGetErrorString(err);
RelAssert(0);
}
glfwSwapInterval(1); // Enable vsync
glfwSetWindowUserPointer(window, window_ctx);
glfwSetKeyCallback(window, key_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetScrollCallback(window, scroll_callback);
}
void applyAppearance(tp::glw::Window::Appearence& appear, tp::glw::Window::Appearence& prev, tp::glw::Window* win_ctx) {
if (!(appear.mSize == prev.mSize)) {
glfwSetWindowSize(window, appear.mSize.x, appear.mSize.y);
prev.mSize = appear.mSize;
}
if (!(appear.mPos == prev.mPos)) {
//glfwSetWindowPos(window, appear.mPos.x, appear.mPos.y);
win_ctx->mEvents.mCursor -= appear.mPos - prev.mPos;
//win_ctx->mEvents.mCursorPrev -= appear.mPos - prev.mPos;
prev.mPos = appear.mPos;
}
if (!(appear.mSizeMax == prev.mSizeMax && appear.mSizeMin == prev.mSizeMin)) {
glfwSetWindowSizeLimits(window, appear.mSizeMin.x, appear.mSizeMin.y, appear.mSizeMax.x, appear.mSizeMax.y);
prev.mSizeMax = appear.mSizeMax;
prev.mSizeMin = appear.mSizeMin;
}
if (!(appear.mMaximized == prev.mMaximized)) {
DBG_BREAK(1);
}
if (!(appear.mMinimized == prev.mMinimized)) {
DBG_BREAK(1);
}
int width, height;
glfwGetWindowSize(window, &width, &height);
appear.mSize = { width, height };
prev.mSize = appear.mSize;
glfwGetWindowPos(window, &width, &height);
appear.mPos = { width, height };
prev.mPos = appear.mPos;
}
void beginDraw() {
//int display_w, display_h;
//glfwGetFramebufferSize(window, &display_w, &display_h);
//glViewport(0, 0, display_w, display_h);
//glClearColor(0, 0, 0, 0);
//glClear(GL_COLOR_BUFFER_BIT);
}
void endDraw() {
glfwSwapBuffers(window);
}
void pollEvents() {
glfwPollEvents();
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
window_ctx->mEvents.mCursorPrev = window_ctx->mEvents.mCursor;
window_ctx->mEvents.mCursor = { xpos, ypos };
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
auto self = (tp::glw::Window*) glfwGetWindowUserPointer(window);
if (action != GLFW_PRESS && action != GLFW_RELEASE) {
return;
}
auto state = (action == GLFW_RELEASE) ? tp::KeyEvent::EventState::RELEASED : tp::KeyEvent::EventState::PRESSED;
//* VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39)
if (key >= GLFW_KEY_0 && key <= GLFW_KEY_9) {
self->mEvents.mKeysQueue.push(tp::KeyEvent{ tp::Keycode(key), state });
return;
}
//* VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A)
else if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z) {
self->mEvents.mKeysQueue.push(tp::KeyEvent{ tp::Keycode(key), state });
return;
}
switch (key) {
MAP_EV(GLFW_KEY_LEFT_SHIFT, tp::Keycode::LEFT_SHIFT);
MAP_EV(GLFW_KEY_LEFT, tp::Keycode::LEFT);
MAP_EV(GLFW_KEY_RIGHT, tp::Keycode::RIGHT);
MAP_EV(GLFW_KEY_UP, tp::Keycode::UP);
MAP_EV(GLFW_KEY_DOWN, tp::Keycode::DOWN);
MAP_EV(GLFW_KEY_BACKSPACE, tp::Keycode::BACKSPACE);
MAP_EV(GLFW_KEY_LEFT_CONTROL, tp::Keycode::LEFT_CONTROL);
MAP_EV(GLFW_KEY_MENU, tp::Keycode::LEFT_ALT);
MAP_EV(GLFW_KEY_ENTER, tp::Keycode::ENTER);
MAP_EV(GLFW_KEY_SPACE, tp::Keycode::SPACE);
MAP_EV(GLFW_KEY_COMMA, tp::Keycode::COMMA);
MAP_EV(GLFW_KEY_PERIOD, tp::Keycode::PERIOD);
MAP_EV(GLFW_KEY_TAB, tp::Keycode::TAB);
MAP_EV(GLFW_KEY_EQUAL, tp::Keycode::EQUAL);
MAP_EV(GLFW_KEY_MINUS, tp::Keycode::MINUS);
MAP_EV(GLFW_KEY_3, tp::Keycode::TILDA);
MAP_EV(GLFW_KEY_4, tp::Keycode::BRA);
MAP_EV(GLFW_KEY_5, tp::Keycode::SLASH);
MAP_EV(GLFW_KEY_6, tp::Keycode::KET);
MAP_EV(GLFW_KEY_1, tp::Keycode::DOUBLE_DOT);
MAP_EV(GLFW_KEY_7, tp::Keycode::QUOTES);
MAP_EV(GLFW_KEY_2, tp::Keycode::INV_SLASH);
MAP_EV(GLFW_KEY_ESCAPE, tp::Keycode::ESCAPE);
}
}
static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
auto self = (tp::glw::Window*)glfwGetWindowUserPointer(window);
if (action != GLFW_PRESS && action != GLFW_RELEASE) {
return;
}
auto state = (action == GLFW_RELEASE) ? tp::KeyEvent::EventState::RELEASED : tp::KeyEvent::EventState::PRESSED;
switch (button) {
case GLFW_MOUSE_BUTTON_RIGHT: {
self->mEvents.mKeysQueue.push(tp::KeyEvent{ tp::Keycode::MOUSE2, state });
} break;
case GLFW_MOUSE_BUTTON_LEFT: {
self->mEvents.mKeysQueue.push(tp::KeyEvent{ tp::Keycode::MOUSE1, state });
} break;
case GLFW_MOUSE_BUTTON_MIDDLE: {
self->mEvents.mKeysQueue.push(tp::KeyEvent{ tp::Keycode::MOUSE3, state });
} break;
}
}
static void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
}
~PlatformContext() {
glfwDestroyWindow(window);
}
};
};
};
void tp::glw::Window::deinit() {}
tp::glw::Window::Window() {
initialize();
mAppearence.mSize = tp::glw::Window::mDevice.Size / 1.5f;
mAppearence.mPos = (tp::glw::Window::mDevice.Size - mAppearence.mSize) / 2.f;
mAppearence.mSizeMax = tp::glw::Window::mDevice.Size;
applyAppearance();
}
tp::glw::Window::Window(const Appearence& aAppearence) {
mAppearence = aAppearence;
initialize();
}
void tp::glw::Window::initialize() {
mPlatformCtx = new PlatformContext(this);
applyAppearance();
}
void tp::glw::Window::applyAppearance() {
mPlatformCtx->applyAppearance(mAppearence, this->mCache.mAppearencePrev, this);
}
void tp::glw::Window::pollEvents() {
applyAppearance();
mPlatformCtx->pollEvents();
mEvents.mRedraw |= (mEvents.mCursorPrev - mEvents.mCursor) != 0;
mEvents.mRedraw |= mEvents.mKeysQueue.length;
}
void tp::glw::Window::platformCallback() {}
void tp::glw::Window::beginDraw() {
mPlatformCtx->beginDraw();
}
void tp::glw::Window::endDraw() {
mPlatformCtx->endDraw();
if (mEvents.mRedraw-- < 0) mEvents.mRedraw = 0;
}
void* tp::glw::Window::platform_window() const {
return mPlatformCtx->window;
}
tp::alni tp::glw::Window::sizeAllocatedMem() {
return 0;
}
tp::alni tp::glw::Window::sizeUsedMem() {
return 0;
}
tp::glw::Window::~Window() {
delete mPlatformCtx;
}

View file

@ -0,0 +1,170 @@
#include "stringt.h"
#include "array.h"
#include "array.h"
#include "filesystem.h"
#include "shader.h"
#include "new.h"
#include "GraphicsLibApi.h"
#include <cstdio>
namespace tp {
using namespace glw;
//string shader_path = "../rsc/shaders/";
shader::shader() {
programm = 0;
VertexShaderID = 0;
FragmentShaderID = 0;
GeometryShaderID = 0;
}
void shader::vert_bind_source(const char* vert_src) {
VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
compile_shader(vert_src, VertexShaderID);
}
void shader::frag_bind_source(const char* frag_src) {
FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
compile_shader(frag_src, FragmentShaderID);
}
void shader::geom_bind_source(const char* geom_src) {
GeometryShaderID = glCreateShader(GL_GEOMETRY_SHADER);
compile_shader(geom_src, GeometryShaderID);
}
void shader::compile() {
GLint Result = GL_FALSE;
int InfoLogLength;
programm = glCreateProgram();
glAttachShader(programm, VertexShaderID);
if (GeometryShaderID) glAttachShader(programm, GeometryShaderID);
glAttachShader(programm, FragmentShaderID);
glLinkProgram(programm);
// Check the program
glGetProgramiv(programm, GL_LINK_STATUS, &Result);
glGetProgramiv(programm, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (InfoLogLength > 0) {
Array<char> ProgramErrorMessage(InfoLogLength + 1);
glGetProgramInfoLog(programm, InfoLogLength, NULL, &ProgramErrorMessage[0]);
printf("%s\n", &ProgramErrorMessage[0]);
}
glDetachShader(programm, VertexShaderID);
glDetachShader(programm, FragmentShaderID);
if (GeometryShaderID) glDetachShader(programm, GeometryShaderID);
glDeleteShader(VertexShaderID);
glDeleteShader(FragmentShaderID);
if (GeometryShaderID) glDeleteShader(GeometryShaderID);
}
bool shader::compile_shader(const char* ShaderCode, uint4 ShaderID) {
GLint Result = GL_FALSE;
int InfoLogLength;
char const* SourcePointer = ShaderCode;
glShaderSource(ShaderID, 1, &SourcePointer, NULL);
glCompileShader(ShaderID);
// Check Shader
glGetShaderiv(ShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(ShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (InfoLogLength > 0) {
Array<char> VertexShaderErrorMessage(InfoLogLength + 1);
glGetShaderInfoLog(ShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
printf("%s\n", &VertexShaderErrorMessage[0]);
}
return Result;
}
void shader::load(const char* pvert, const char* pgeom, const char* pfrag, bool paths) {
// Create the shaders
VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
if (pgeom) GeometryShaderID = glCreateShader(GL_GEOMETRY_SHADER);
else GeometryShaderID = 0;
GLint Result = GL_FALSE;
int InfoLogLength = 0;
if (paths) {
string vert = sfmt("%c.vert", pvert);
string geom = pgeom ? sfmt("%c.geom", pgeom) : " ";
string frag = sfmt("%c.frag", pfrag);
tp::string content;
printf("Compiling shader : %s\n", vert.cstr());
if (content.loadFile(vert)) {
compile_shader(content.cstr(), VertexShaderID);
}
if (GeometryShaderID) {
printf("Compiling shader : %s\n", geom.cstr());
if (content.loadFile(geom)) {
compile_shader(content.cstr(), GeometryShaderID);
}
}
printf("Compiling shader : %s\n", frag.cstr());
if (content.loadFile(frag)) {
compile_shader(content.cstr(), FragmentShaderID);
}
}
else {
compile_shader(pvert, VertexShaderID);
if (GeometryShaderID) {
compile_shader(pgeom, GeometryShaderID);
}
compile_shader(pfrag, FragmentShaderID);
}
compile();
}
shader::shader(const char* vert, const char* geom, const char* frag, bool paths) {
load(vert, geom, frag, paths);
}
void shader::bind() {
glUseProgram(programm);
}
GLuint glw::shader::getu(const char* uid) {
return glGetUniformLocation(programm, uid);
}
void shader::unbind() {
glUseProgram(0);
}
shader::~shader() {
glDeleteProgram(programm);
}
alni glw::shader::sizeAllocatedMem() {
return sizeof(GLuint) * 4;
}
alni glw::shader::sizeUsedMem() {
return sizeof(GLuint) * 4;
}
};

View file

@ -0,0 +1,164 @@
#pragma once
#include "simplegui.h"
using namespace tp;
using namespace glw;
void WindowSimpleInputs::update(const glw::Window& window, halnf uiscale) {
mScaleVal = (window.mDevice.mDPMM * uiscale);
mWindowSizeMM = window.mAppearence.mSize / mScaleVal;
mCrs = window.mEvents.mCursor / mScaleVal;
mCrsPrev = window.mEvents.mCursorPrev / mScaleVal;
mCrsmDelta = mCrs - mCrsPrev;
if (mMouse == Mouse::PRESSED) {
mMouse = Mouse::HOLD;
}
else if (mMouse == Mouse::RELEASED) {
mMouse = Mouse::NONE;
}
mMove = Move::NONE;
for (auto event : window.mEvents.mKeysQueue) {
bool press = event.data().event_state == KeyEvent::EventState::PRESSED;
if (press) {
switch (event.data().code) {
case Keycode::UP: mMove = Move::UP; break;
case Keycode::DOWN: mMove = Move::DOWN; break;
case Keycode::LEFT: mMove = Move::LEFT; break;
case Keycode::RIGHT: mMove = Move::RIGHT; break;
}
}
if (event.data().code == Keycode::MOUSE1) {
if (press) {
mMouse = Mouse::PRESSED;
}
else {
mMouse = Mouse::RELEASED;
}
}
}
}
bool WindowSimpleInputs::Activated() { return mMouse == Mouse::PRESSED; }
bool WindowSimpleInputs::Anticipating() { return mMouse == Mouse::HOLD; }
bool WindowSimpleInputs::Selected() { return mMouse == Mouse::RELEASED; }
bool WindowSimpleInputs::MoveLeft() { return mMove == Move::LEFT; }
bool WindowSimpleInputs::MoveRight() { return mMove == Move::RIGHT; }
bool WindowSimpleInputs::MoveUp() { return mMove == Move::UP; }
bool WindowSimpleInputs::MoveDown() { return mMove == Move::DOWN; }
bool WindowSimpleInputs::Drag() { return mCrsmDelta != 0; }
void ButtonWidget::draw(glw::Canvas& canvas) {
canvas.setCol1(mCol);
canvas.rect(mAnimRec.get(), 1.f);
}
void ButtonWidget::proc(glw::WindowSimpleInputs& inputs) {
auto rec = mAnimRec.get();
mPressed = false;
mAnimRec.setAnimTime(100);
if (rec.inside(inputs.mCrs)) {
if (inputs.Anticipating()) {
auto new_rec = mRect;
new_rec.scale_from_center(0.3f, true);
mAnimRec.setNoTransition(new_rec);
}
else {
mAnimRec.set(mRect);
}
if (inputs.Selected()) {
mPressed = true;
}
}
else {
auto new_rec = mRect;
new_rec.scale_from_center(0.3f, true);
mAnimRec.set(new_rec);
}
}
void CheckBoxWidget::draw(glw::Canvas& canvas) {
canvas.setCol1(mAnimCol.get());
canvas.rect(mAnimRec.get(), 1.f);
}
void CheckBoxWidget::proc(glw::WindowSimpleInputs& inputs) {
auto rec = mAnimRec.get();
mAnimRec.setAnimTime(100);
if (rec.inside(inputs.mCrs)) {
if (inputs.Anticipating()) {
auto new_rec = mRect;
new_rec.scale_from_center(0.3f, true);
mAnimRec.setNoTransition(new_rec);
}
else {
mAnimRec.set(mRect);
}
if (inputs.Selected()) {
mValue = !mValue;
}
}
else {
auto new_rec = mRect;
new_rec.scale_from_center(0.3f, true);
mAnimRec.set(new_rec);
}
if (mValue) {
mAnimCol.set(mColActive);
}
else {
mAnimCol.set(mCol);
}
}
void WindowsHeaderWidget::updRecs() {
auto padding = 0.7f;
auto butt_size = header_rec.w - padding * 2.f;
grab_rec = { header_rec.pos, { header_rec.z - header_rec.w * 4, header_rec.w } };
mPinButton.mRect = { { grab_rec.pos.x + grab_rec.size.x + padding, grab_rec.y + padding }, butt_size };
mMinButton.mRect = { { mPinButton.mRect.pos.x + mPinButton.mRect.size.x + padding * 2, mPinButton.mRect.y }, butt_size };
mMaxButton.mRect = { { mMinButton.mRect.pos.x + mMinButton.mRect.size.x + padding * 2, mPinButton.mRect.y }, butt_size };
mExitButton.mRect = { { mMaxButton.mRect.pos.x + mMaxButton.mRect.size.x + padding * 2, mPinButton.mRect.y }, butt_size };
mPinButton.mCol = { 0.3f, 0.3f, 0.3f, 0.9f};
mMinButton.mCol = { 0.3f, 0.3f, 0.8f, 0.9f };
mMaxButton.mCol = { 0.3f, 0.8f, 0.3f, 0.9f };
mExitButton.mCol = { 0.8f, 0.3f, 0.3f, 0.9f };
}
void WindowsHeaderWidget::draw(glw::Canvas& canvas) {
updRecs();
canvas.setCol1(col);
canvas.rect(header_rec, 1.f);
canvas.setCol1(1.f);
canvas.text("MemLeaks", grab_rec, 4.f);
mExitButton.draw(canvas);
mMaxButton.draw(canvas);
mMinButton.draw(canvas);
mPinButton.draw(canvas);
}
void WindowsHeaderWidget::proc(glw::Window& window, glw::WindowSimpleInputs& inputs) {
get_time();
mExitButton.proc(inputs);
mMaxButton.proc(inputs);
mMinButton.proc(inputs);
mPinButton.proc(inputs);
window.mAppearence.mMinMaxBox = mMaxButton.mRect * inputs.mScaleVal;
window.mAppearence.mCaptionsAddArea = { grab_rec * inputs.mScaleVal };
window.mAppearence.mMinimized = mMinButton.mPressed;
window.mEvents.mClose = mExitButton.mPressed;
window.mAppearence.mTopZ = mPinButton.mValue;
}

View file

@ -0,0 +1,216 @@
#include "texture.h"
#include "map.h"
#include "stringt.h"
#include "shader.h"
#include "GraphicsLibApi.h"
const char* texture_vertex =
#ifdef ENV_OS_ANDROID
"#version 300 es\n"
"precision mediump float;\n"
#else
"#version 330 core\n"
#endif
"layout(location = 0) in vec3 vPos;\n"
"out vec2 UV;\n"
"void main() {\n"
" gl_Position = vec4(vPos, 1);\n"
" UV = (vPos.xy + vec2(1, 1)) / 2.0;\n"
"}\n";
const char* texture_fragment =
#ifdef ENV_OS_ANDROID
"#version 300 es\n"
"precision mediump float;\n"
#else
"#version 330 core\n"
#endif
"in vec2 UV;\n"
"out vec4 color;\n"
"uniform sampler2D renderedTexture;\n"
"uniform float time;\n"
"void main() {\n"
" vec4 texColor = texture(renderedTexture, UV);\n"
" if (texColor.a < 0.2)\n"
" discard;\n"
" color = texColor;\n"
"}\n";
namespace tp {
using namespace glw;
GLuint texture::getid() { return id; }
texture::texture() {
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
texture::~texture() { glDeleteTextures(1, &id); }
alni texture::sizeAllocatedMem() {
return sizeof(GLuint);
}
alni texture::sizeUsedMem() {
return sizeof(GLuint);
}
void texture::update(const Array2D<rgba>& buff) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)buff.size().x, (GLsizei)buff.size().y, 0, GL_RGBA, GL_FLOAT, buff.buff());
}
void texture::draw(const GLuint& out) {
draw_texture(out, id);
}
struct texture_drawer_data {
HashMap<GLuint, string> textures;
GLuint quad_VertexArrayID;
GLuint quad_vertexbuffer;
GLuint texID;
glw::shader shader;
const GLfloat g_quad_vertex_buffer_data[18] = {
-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
};
texture_drawer_data() : shader(texture_vertex, NULL, texture_fragment, false) {
// The fullscreen quad's FBO
glGenVertexArrays(1, &quad_VertexArrayID);
glBindVertexArray(quad_VertexArrayID);
glGenBuffers(1, &quad_vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data),
g_quad_vertex_buffer_data, GL_STATIC_DRAW);
texID = shader.getu("renderedTexture");
}
~texture_drawer_data() {
glDeleteBuffers(1, &quad_vertexbuffer);
glDeleteVertexArrays(1, &quad_VertexArrayID);
for (auto tex : textures) {
glDeleteTextures(1, &tex->val);
}
}
};
texture_drawer_data* texdd = NULL;
void texture::init() {
if (!texdd) texdd = new texture_drawer_data();
}
void texture::deinit() {
if (texdd) delete texdd;
texdd = NULL;
}
void texture::draw_texture(uint4 out, uint4 in) {
assert(in);
// Render to the screen
glBindFramebuffer(GL_FRAMEBUFFER, out);
// Use our shader
texdd->shader.bind();
// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, in);
// Set our "renderedTexture" sampler to use Texture Unit 0
glUniform1i(texdd->texID, 0);
// glUniformMatrix4fv(texdd->rect_mat, 1, GL_FALSE, &tmat[0][0]);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, texdd->quad_vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// Draw the triangles. 2*3 indices starting at 0 -> 2 triangles
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
texdd->shader.unbind();
}
GLuint load_texture(string name) {
GLuint tex_2d = 0;
RelAssert(0 && "incomplete compilation - no SOIL support added");
if (0) {
//auto document = lunasvg::Document::loadFromFile("tiger.svg");
//auto bitmap = document->renderToBitmap();
}
else {
/*
tex_2d = SOIL_load_OGL_texture(
name.cstr(), SOIL_LOAD_RGBA, SOIL_CREATE_NEW_ID,
SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB |
SOIL_FLAG_COMPRESS_TO_DXT);
if (0 == tex_2d) {
printf("SOIL loading error: '%s'\n", SOIL_last_result());
}
*/
}
return tex_2d;
}
GLuint texture::get_tex(const char* TexId) {
GLuint out = 0;
alni idx = texdd->textures.presents(TexId);
if (idx != -1) {
out = texdd->textures.get(TexId);
}
else {
out = load_texture(TexId);
texdd->textures.put(TexId, out);
}
return out;
}
void texture::drawCurcle(vec2f pos, double radius, rgba col) {
#ifndef ENV_OS_ANDROID
static alni precision = 40;
glColor4f(col.r, col.g, col.b, col.a);
double twicePi = 2.0 * 3.142;
glBegin(GL_TRIANGLE_FAN); // BEGIN CIRCLE
glVertex2f(pos.x, pos.y); // center of circle
for (alni i = 0; i <= precision; i++) {
glVertex2f((GLfloat)(pos.x + (radius * cos(i * twicePi / precision))),
(GLfloat)(pos.y + (radius * sin(i * twicePi / precision))));
}
glEnd(); // END
#endif
}
};

Binary file not shown.

View file

@ -0,0 +1,107 @@
#include "SimpleGui.h"
#include "debugui.h"
#include "texture.h"
#include "timer.h"
#include "imgui.h"
struct CollorWheelWidget {
tp::rgba col = { 0.3f, 0.3f, 0.3f, 0.9f };
tp::rectf rec = { 10, 30, 40, 40 };
void draw(tp::glw::Canvas& canvas) {
canvas.setCol1(col);
canvas.rect(rec, 3.f);
canvas.drawColorwheel(rec, col.rgbs);
}
};
struct Test {
tp::glw::Window window;
tp::glw::DebugUI ui;
tp::glw::Canvas canvas;
tp::glw::WindowsHeaderWidget header;
tp::glw::WindowSimpleInputs inputs;
tp::halnf mHeaderHeight = 6.5f;
CollorWheelWidget colorwheel;
tp::halnf uiscale = 1.f;
tp::Timer time;
tp::halni frames = 0;
tp::halni fps = 0;
Test() : window(), ui(window, debuguiCallBack, this), time(1000) {
tp::glw::texture::init();
canvas.setCol1({ 0.5f, 0.5f, 0.5f, 0.5f });
window.mAppearence.mHiden = false;
}
void proc() {
window.pollEvents();
inputs.update(window, uiscale);
header.header_rec = { (inputs.mWindowSizeMM.x - 60.f) / 2.f, 3.f, 60.f, mHeaderHeight };
header.proc(window, inputs);
}
void draw() {
// window frame
window.beginDraw(); {
// canvas draw
canvas.beginDraw({ { 0, 0 }, inputs.mWindowSizeMM }, window.mDevice.mDPMM, uiscale); {
canvas.clear();
header.draw(canvas);
colorwheel.draw(canvas);
} canvas.endDraw();
// debug draw
ui.drawDebugUI(window.mDevice.mDPMM);
} window.endDraw();
}
void run() {
while (!window.mEvents.mClose) {
proc();
if (window.mEvents.mRedraw || tp::glw::gInTransition) {
draw();
}
if (time.isTimeout()) {
fps = frames;
frames = 0;
time.reset();
}
frames++;
}
}
void debugui() {
static char buff[100];
ImGui::DragFloat("UI SCale", &uiscale, 0.02);
ImGui::Text("Fps: %i", fps);
ImGui::InputText("Fps: %i", buff, 100);
}
static void debuguiCallBack(void* self) {
((Test*)self)->debugui();
}
~Test() {
tp::glw::texture::deinit();
}
};
CROSSPLATFORM_MAIN
int main() {
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleGlw, NULL };
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
if (!TestModule.initialize()) {
return 1;
}
{
Test test;
test.run();
}
TestModule.deinitialize();
}