7414 lines
224 KiB
C
7414 lines
224 KiB
C
//This file was automatically generated by Makefile at https://github.com/cntools/rawdraw
|
|
//Generated from files git hash f90dd987fef2a5efde5ea05251f1d72e07e91a5d on Wed Feb 8 12:11:21 PST 2023 (This is not the git hash of this file)
|
|
// Copyright 2010-2021 <>< CNLohr, et. al. (Several other authors, many but not all mentioned)
|
|
// Licensed under the MIT/x11 or NewBSD License you choose.
|
|
//
|
|
// CN Foundational Graphics Main Header File. This is the main header you
|
|
// should include. See README.md for more details.
|
|
|
|
|
|
#ifndef _CNFG_H
|
|
#define _CNFG_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Rawdraw flags:
|
|
CNFG3D -> Enable the weird 3D functionality that rawdraw has to allow you to
|
|
write apps which emit basic rawdraw primitives but look 3D!
|
|
CNFG_USE_DOUBLE_FUNCTIONS -> Use double-precision floating point for CNFG3D.
|
|
CNFGOGL -> Use an OpenGL Backend for all rawdraw functionality.
|
|
->Caveat->If using CNFG_HAS_XSHAPE, then, we do something realy wacky.
|
|
CNFGRASTERIZER -> Software-rasterize the rawdraw calls, and, use
|
|
CNFGUpdateScreenWithBitmap to send video to webpage.
|
|
CNFGCONTEXTONLY -> Don't add any drawing functions, only opening a window to
|
|
get an OpenGL context.
|
|
|
|
CNFG_IMPLEMENTATION -> #define this and it will make _this_ the file where the cnfg
|
|
functions actually live.
|
|
|
|
Usually tested combinations:
|
|
* TCC On Windows and X11 (Linux) with:
|
|
- CNFGOGL on or CNFGOGL off. If CNFGOGL is off you can use
|
|
CNFG_WINDOWS_DISABLE_BATCH to disable all batching.
|
|
-or-
|
|
- CNFGRASTERIZER
|
|
|
|
NOTE: Sometimes you can also use CNFGOGL + CNFGRASTERIZER
|
|
|
|
* WASM driver supports both: CNFGRASTERIZER and without CNFGRASTERIZER (Recommended turn rasterizer off)
|
|
* ANDROID (But this automatically sets CNFGRASTERIZER OFF and CNFGOGL ON)
|
|
|
|
Unusual compiler flags:
|
|
|
|
* CNFGHTTP - Enable the HTTP server-version of rawdraw, where it renders to a website.
|
|
* CNFGHTTPSERVERONLY - if you want to use the HTTP server w/o rawdraw. You will need to implement:
|
|
- CloseEvent, HTTPCustomCallback, HTTPCustomStart, NewWebSocket, WebSocketData, WebSocketTick
|
|
* CNFG_DISABLE_HTTP_FILES - disable the HTTP file server.
|
|
|
|
*/
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
//Some per-platform logic.
|
|
#if defined( ANDROID ) || defined( __android__ )
|
|
#define CNFGOGL
|
|
#endif
|
|
|
|
#if ( defined( CNFGOGL ) || defined( __wasm__ ) ) && !defined(CNFG_HAS_XSHAPE)
|
|
|
|
#define CNFG_BATCH 8192 //131,072 bytes.
|
|
|
|
#if defined( ANDROID ) || defined( __android__ ) || defined( __wasm__ ) || defined( EGL_LEAN_AND_MEAN )
|
|
#define CNFGEWGL //EGL or WebGL
|
|
#else
|
|
#define CNFGDESKTOPGL
|
|
#endif
|
|
#endif
|
|
|
|
typedef struct {
|
|
short x, y;
|
|
} RDPoint;
|
|
|
|
extern int CNFGPenX, CNFGPenY;
|
|
extern uint32_t CNFGBGColor;
|
|
extern uint32_t CNFGLastColor;
|
|
extern uint32_t CNFGDialogColor; //Only used for DrawBox
|
|
|
|
//Draws text at CNFGPenX, CNFGPenY, with scale of `scale`.
|
|
void CNFGDrawText( const char * text, short scale );
|
|
|
|
//Determine how large a given test would be to draw.
|
|
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize );
|
|
|
|
//Draws a box, outline as whatever the last CNFGColor was set to but also draws
|
|
//a rectangle as a background as whatever CNFGDialogColor is set to.
|
|
void CNFGDrawBox( short x1, short y1, short x2, short y2 );
|
|
|
|
//To be provided by driver. Rawdraw uses colors in the format 0xRRGGBBAA
|
|
//Note that some backends do not support alpha of any kind.
|
|
//Some platforms also support alpha blending. So, be sure to set alpha to 0xFF
|
|
uint32_t CNFGColor( uint32_t RGBA );
|
|
|
|
//This both updates the screen, and flips, all as a single operation.
|
|
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h );
|
|
|
|
|
|
//This is only supported on a FEW architectures, but allows arbitrary
|
|
//image blitting. Note that the alpha channel behavior is different
|
|
//on different systems.
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h );
|
|
|
|
// Only supported with CNFGOGL
|
|
#ifdef CNFGOGL
|
|
void CNFGDeleteTex( unsigned int tex );
|
|
unsigned int CNFGTexImage( uint32_t *data, int w, int h );
|
|
void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h );
|
|
#endif
|
|
|
|
void CNFGTackPixel( short x1, short y1 );
|
|
void CNFGTackSegment( short x1, short y1, short x2, short y2 );
|
|
void CNFGTackRectangle( short x1, short y1, short x2, short y2 );
|
|
void CNFGTackPoly( RDPoint * points, int verts );
|
|
void CNFGClearFrame();
|
|
void CNFGSwapBuffers();
|
|
|
|
void CNFGGetDimensions( short * x, short * y );
|
|
|
|
|
|
//This will setup a window. Note that w and h have special meaning. On Windows
|
|
//and X11, for instance if you set w and h to be negative, then rawdraw will not
|
|
//show the window to the user. This is useful if you just need it for some
|
|
//off-screen-rendering purpose.
|
|
//
|
|
//Return value of 0 indicates success. Nonzero indicates error.
|
|
int CNFGSetup( const char * WindowName, int w, int h );
|
|
|
|
void CNFGSetupFullscreen( const char * WindowName, int screen_number );
|
|
int CNFGHandleInput();
|
|
|
|
|
|
//You must provide:
|
|
void HandleKey( int keycode, int bDown );
|
|
void HandleButton( int x, int y, int button, int bDown );
|
|
void HandleMotion( int x, int y, int mask );
|
|
void HandleDestroy();
|
|
|
|
//Internal function for resizing rasterizer for rasterizer-mode.
|
|
void CNFGInternalResize( short x, short y ); //don't call this.
|
|
|
|
//Not available on all systems. Use The OGL portion with care.
|
|
#ifdef CNFGOGL
|
|
void CNFGSetVSync( int vson );
|
|
void * CNFGGetExtension( const char * extname );
|
|
#endif
|
|
|
|
//Also not available on all systems. Transparency.
|
|
void CNFGPrepareForTransparency();
|
|
void CNFGDrawToTransparencyMode( int transp );
|
|
void CNFGClearTransparencyLevel();
|
|
|
|
//Only available on systems that support it.
|
|
void CNFGSetLineWidth( short width );
|
|
void CNFGChangeWindowTitle( const char * windowtitle );
|
|
void CNFGSetWindowIconData( int w, int h, uint32_t * data );
|
|
int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ );
|
|
|
|
//If you're using a batching renderer, for instance on Android or an OpenGL
|
|
//You will need to call this function inbetewen swtiching properties of drawing. This is usually
|
|
//only needed if you calling OpenGL / OGLES functions directly and outside of CNFG.
|
|
//
|
|
//Note that these are the functions that are used on the backends which support this
|
|
//sort of thing.
|
|
#ifdef CNFG_BATCH
|
|
|
|
//If you are not using the CNFGOGL driver, you will need to define these in your driver.
|
|
void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices );
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h );
|
|
|
|
//These need to be defined for the specific driver.
|
|
void CNFGClearFrame();
|
|
void CNFGSwapBuffers();
|
|
|
|
void CNFGFlushRender(); //Emit any geometry (lines, squares, polys) which are slated to be rendered.
|
|
void CNFGInternalResize( short x, short y ); //Driver calls this after resize happens.
|
|
void CNFGSetupBatchInternal(); //Driver calls this after setup is complete.
|
|
|
|
//Useful function for emitting a non-axis-aligned quad.
|
|
void CNFGEmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 );
|
|
|
|
extern int CNFGVertPlace;
|
|
extern float CNFGVertDataV[CNFG_BATCH*3];
|
|
extern uint32_t CNFGVertDataC[CNFG_BATCH];
|
|
#endif
|
|
|
|
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
|
|
#define CNFG_KEY_SHIFT 0x10
|
|
#define CNFG_KEY_BACKSPACE 0x08
|
|
#define CNFG_KEY_DELETE 0x2E
|
|
#define CNFG_KEY_LEFT_ARROW 0x25
|
|
#define CNFG_KEY_RIGHT_ARROW 0x27
|
|
#define CNFG_KEY_TOP_ARROW 0x26
|
|
#define CNFG_KEY_BOTTOM_ARROW 0x28
|
|
#define CNFG_KEY_ESCAPE 0x1B
|
|
#define CNFG_KEY_ENTER 0x0D
|
|
|
|
#elif defined( EGL_LEAN_AND_MEAN ) // doesn't have any keys
|
|
#elif defined( __android__ ) || defined( ANDROID ) // ^
|
|
#elif defined( __wasm__ )
|
|
|
|
#define CNFG_KEY_SHIFT 16
|
|
#define CNFG_KEY_BACKSPACE 8
|
|
#define CNFG_KEY_DELETE 46
|
|
#define CNFG_KEY_LEFT_ARROW 37
|
|
#define CNFG_KEY_RIGHT_ARROW 39
|
|
#define CNFG_KEY_TOP_ARROW 38
|
|
#define CNFG_KEY_BOTTOM_ARROW 40
|
|
#define CNFG_KEY_ESCAPE 27
|
|
#define CNFG_KEY_ENTER 13
|
|
|
|
#else // most likely x11
|
|
|
|
#define CNFG_KEY_SHIFT 65505
|
|
#define CNFG_KEY_BACKSPACE 65288
|
|
#define CNFG_KEY_DELETE 65535
|
|
#define CNFG_KEY_LEFT_ARROW 65361
|
|
#define CNFG_KEY_RIGHT_ARROW 65363
|
|
#define CNFG_KEY_TOP_ARROW 65362
|
|
#define CNFG_KEY_BOTTOM_ARROW 65364
|
|
#define CNFG_KEY_ESCAPE 65307
|
|
#define CNFG_KEY_ENTER 65293
|
|
#define CNFG_X11_EXPOSE 0xff00 //65280
|
|
|
|
#endif
|
|
|
|
#ifdef CNFG3D
|
|
|
|
#ifndef __wasm__
|
|
#include <math.h>
|
|
#endif
|
|
|
|
#ifdef CNFG_USE_DOUBLE_FUNCTIONS
|
|
#define tdCOS cos
|
|
#define tdSIN sin
|
|
#define tdTAN tan
|
|
#define tdSQRT sqrt
|
|
#else
|
|
#define tdCOS cosf
|
|
#define tdSIN sinf
|
|
#define tdTAN tanf
|
|
#define tdSQRT sqrtf
|
|
#endif
|
|
|
|
#ifdef __wasm__
|
|
void tdMATCOPY( float * x, const float * y ); //Copy y into x
|
|
#else
|
|
#define tdMATCOPY(x,y) memcpy( x, y, 16*sizeof(float))
|
|
#endif
|
|
|
|
#define tdQ_PI 3.141592653589
|
|
#define tdDEGRAD (tdQ_PI/180.)
|
|
#define tdRADDEG (180./tdQ_PI)
|
|
|
|
|
|
//General Matrix Functions
|
|
void tdIdentity( float * f );
|
|
void tdZero( float * f );
|
|
void tdTranslate( float * f, float x, float y, float z ); //Operates ON f
|
|
void tdScale( float * f, float x, float y, float z ); //Operates ON f
|
|
void tdRotateAA( float * f, float angle, float x, float y, float z ); //Operates ON f
|
|
void tdRotateQuat( float * f, float qw, float qx, float qy, float qz ); //Operates ON f
|
|
void tdRotateEA( float * f, float x, float y, float z ); //Operates ON f
|
|
void tdMultiply( float * fin1, float * fin2, float * fout ); //Operates ON f
|
|
void tdPrint( const float * f );
|
|
void tdTransposeSelf( float * f );
|
|
|
|
//Specialty Matrix Functions
|
|
void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out ); //Sets, NOT OPERATES. (FOVX=degrees)
|
|
void tdLookAt( float * m, float * eye, float * at, float * up ); //Operates ON m
|
|
//General point functions
|
|
#define tdPSet( f, x, y, z ) { f[0] = x; f[1] = y; f[2] = z; }
|
|
void tdPTransform( const float * pin, float * f, float * pout );
|
|
void tdVTransform( const float * vin, float * f, float * vout );
|
|
void td4Transform( float * kin, float * f, float * kout );
|
|
void td4RTransform( float * kin, float * f, float * kout );
|
|
void tdNormalizeSelf( float * vin );
|
|
void tdCross( float * va, float * vb, float * vout );
|
|
float tdDistance( float * va, float * vb );
|
|
float tdDot( float * va, float * vb );
|
|
#define tdPSub( x, y, z ) { (z)[0] = (x)[0] - (y)[0]; (z)[1] = (x)[1] - (y)[1]; (z)[2] = (x)[2] - (y)[2]; }
|
|
#define tdPAdd( x, y, z ) { (z)[0] = (x)[0] + (y)[0]; (z)[1] = (x)[1] + (y)[1]; (z)[2] = (x)[2] + (y)[2]; }
|
|
|
|
//Stack Functionality
|
|
#define tdMATRIXMAXDEPTH 32
|
|
extern float * gSMatrix;
|
|
void tdPush();
|
|
void tdPop();
|
|
void tdMode( int mode );
|
|
#define tdMODELVIEW 0
|
|
#define tdPROJECTION 1
|
|
|
|
//Final stage tools
|
|
void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy );
|
|
void tdFinalPoint( float * pin, float * pout );
|
|
|
|
float tdNoiseAt( int x, int y );
|
|
float tdFLerp( float a, float b, float t );
|
|
float tdPerlin2D( float x, float y );
|
|
|
|
#endif
|
|
|
|
extern const unsigned char RawdrawFontCharData[1405];
|
|
extern const unsigned short RawdrawFontCharMap[256];
|
|
|
|
#ifdef __cplusplus
|
|
};
|
|
#endif
|
|
|
|
|
|
#if defined( ANDROID ) || defined( __android__ )
|
|
#ifndef _CNFG_ANDROID_H
|
|
#define _CNFG_ANDROID_H
|
|
|
|
//This file contains the additional functions that are available on the Android platform.
|
|
//In order to build rawdraw for Android, please compile CNFGEGLDriver.c with -DANDROID
|
|
|
|
// Tricky: Android headers are confused by c++ if linking statically.
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
int __system_property_get(const char* __name, char* __value);
|
|
};
|
|
#endif
|
|
|
|
extern struct android_app * gapp;
|
|
void AndroidMakeFullscreen();
|
|
int AndroidHasPermissions(const char* perm_name);
|
|
void AndroidRequestAppPermissions(const char * perm);
|
|
void AndroidDisplayKeyboard(int pShow);
|
|
int AndroidGetUnicodeChar( int keyCode, int metaState );
|
|
void AndroidSendToBack( int param );
|
|
|
|
extern int android_sdk_version; //Derived at start from property ro.build.version.sdk
|
|
extern int android_width, android_height;
|
|
extern int UpdateScreenWithBitmapOffsetX;
|
|
extern int UpdateScreenWithBitmapOffsetY;
|
|
|
|
// If you need them, these are the names of raw EGL symbols.
|
|
//extern EGLDisplay egl_display;
|
|
//extern EGLSurface egl_surface;
|
|
//extern EGLContext egl_context;
|
|
//extern EGLConfig egl_config;
|
|
|
|
|
|
//You must implement these.
|
|
void HandleResume();
|
|
void HandleSuspend();
|
|
|
|
|
|
//Departures:
|
|
|
|
// HandleMotion's "mask" parameter is actually just an index, not a mask
|
|
|
|
// CNFGSetup / CNFGSetupFullScreen only controls whether or not the navigation
|
|
// decoration is removed. Fullscreen means *full screen* To choose fullscreen
|
|
// or not fullscrene, modify, in your AndroidManifest.xml file, the application
|
|
// section to either contain or not contain:
|
|
// android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
|
|
|
#endif
|
|
|
|
|
|
#endif
|
|
|
|
#ifdef CNFG_IMPLEMENTATION
|
|
//Include this file to get all of rawdraw. You usually will not
|
|
//want to include this in your build, but instead, #include "CNFG.h"
|
|
//after #define CNFG_IMPLEMENTATION in one of your C files.
|
|
|
|
#if defined( CNFGHTTP )
|
|
//Copyright 2015-2021 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or
|
|
// ColorChord License. You Choose. This file mostly based on `cnhttp` from cntools.
|
|
|
|
#ifdef CNFGHTTP
|
|
|
|
//Pull from a buffer
|
|
#ifndef CNFGHTTP_LIVE_FS
|
|
#define USE_RAM_MFS
|
|
#endif
|
|
|
|
//single_file_http.c base from https://github.com/cntools/httptest.
|
|
//scroll to bottom for implementation.
|
|
|
|
#ifndef CUSTOM_HTTPHEADER_CODE
|
|
#define CUSTOM_HTTPHEADER_CODE PushString("Access-Control-Allow-Origin: *\r\n");
|
|
#endif
|
|
|
|
/* public api for steve reid's public domain SHA-1 implementation */
|
|
/* this file is in the public domain */
|
|
|
|
|
|
#include <stdint.h>
|
|
#include <malloc.h>
|
|
|
|
typedef struct {
|
|
uint32_t state[5];
|
|
uint32_t count[2];
|
|
uint8_t buffer[64];
|
|
} RD_SHA1_CTX;
|
|
|
|
#define RD_SHA1_DIGEST_SIZE 20
|
|
|
|
void static RD_SHA1_Init(RD_SHA1_CTX* context);
|
|
void static RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len);
|
|
void static RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context); //WARNINGThe parameters are flipped here. (CNL)
|
|
|
|
//Not to be confused with MFS for the AVR.
|
|
|
|
#ifndef _MFS_H
|
|
#define _MFS_H
|
|
|
|
|
|
#ifndef USE_RAM_MFS
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#define MFS_SECTOR 256
|
|
#define MFS_FILENAMELEN 32-8
|
|
#define MFS_FILE_COMPRESSED_MEMORY (-2)
|
|
|
|
//Format:
|
|
// [FILE NAME (24)] [Start (4)] [Len (4)]
|
|
// NOTE: Filename must be null-terminated within the 24.
|
|
struct MFSFileEntry
|
|
{
|
|
char name[MFS_FILENAMELEN];
|
|
uint32_t start; //From beginning of mfs thing.
|
|
uint32_t len;
|
|
};
|
|
|
|
|
|
struct MFSFileInfo
|
|
{
|
|
uint32_t filelen;
|
|
#ifdef USE_RAM_MFS
|
|
uint32_t offset;
|
|
#else
|
|
FILE * file;
|
|
#endif
|
|
};
|
|
|
|
|
|
|
|
//Returns 0 on succses.
|
|
//Returns size of file if non-empty
|
|
//If positive, populates mfi.
|
|
//Returns -1 if can't find file or reached end of file list.
|
|
int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi );
|
|
int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ); //returns # of bytes left in file.
|
|
void MFSClose( struct MFSFileInfo * mfi );
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
#ifndef _CNHTTP_H
|
|
#define _CNHTTP_H
|
|
|
|
#include <stdint.h>
|
|
|
|
extern struct HTTPConnection * curhttp;
|
|
extern uint8_t * curdata;
|
|
extern uint16_t curlen;
|
|
extern uint8_t wsmask[4];
|
|
extern uint8_t wsmaskplace;
|
|
|
|
|
|
|
|
uint8_t WSPOPMASK();
|
|
#define HTTPPOP (*curdata++)
|
|
|
|
//You must provide this.
|
|
void HTTPCustomStart( );
|
|
void HTTPCustomCallback( ); //called when we can send more data
|
|
void WebSocketData( int len );
|
|
void WebSocketTick( );
|
|
void WebSocketNew();
|
|
void HTTPHandleInternalCallback( );
|
|
uint8_t hex2byte( const char * c );
|
|
void NewWebSocket();
|
|
void et_espconn_disconnect( int socket );
|
|
|
|
//Internal Functions
|
|
void HTTPTick( uint8_t timedtick );
|
|
int URLDecode( char * decodeinto, int maxlen, const char * buf );
|
|
void WebSocketGotData( uint8_t c );
|
|
void WebSocketTickInternal();
|
|
void WebSocketSend( uint8_t * data, int size );
|
|
|
|
//Host-level functions
|
|
void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data );
|
|
void Uint32To10Str( char * out, uint32_t dat );
|
|
void http_recvcb(int conn, char *pusrdata, unsigned short length);
|
|
void http_disconnetcb(int conn);
|
|
int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure
|
|
void DataStartPacket();
|
|
extern uint8_t * databuff_ptr;
|
|
void PushString( const char * data );
|
|
void PushByte( uint8_t c );
|
|
void PushBlob( const uint8_t * datam, int len );
|
|
int TCPCanSend( int socket, int size );
|
|
int TCPDoneSend( int socket );
|
|
int EndTCPWrite( int socket );
|
|
|
|
#define HTTP_CONNECTIONS 50
|
|
#ifndef MAX_HTTP_PATHLEN
|
|
#define MAX_HTTP_PATHLEN 80
|
|
#endif
|
|
#define HTTP_SERVER_TIMEOUT 500
|
|
|
|
|
|
#define HTTP_STATE_NONE 0
|
|
#define HTTP_STATE_WAIT_METHOD 1
|
|
#define HTTP_STATE_WAIT_PATH 2
|
|
#define HTTP_STATE_WAIT_PROTO 3
|
|
|
|
#define HTTP_STATE_WAIT_FLAG 4
|
|
#define HTTP_STATE_WAIT_INFLAG 5
|
|
#define HTTP_STATE_DATA_XFER 7
|
|
#define HTTP_STATE_DATA_WEBSOCKET 8
|
|
|
|
#define HTTP_WAIT_CLOSE 15
|
|
|
|
struct HTTPConnection
|
|
{
|
|
uint8_t state:4;
|
|
uint8_t state_deets;
|
|
|
|
//Provides path, i.e. "/index.html" but, for websockets, the last
|
|
//32 bytes of the buffer are used for the websockets key.
|
|
uint8_t pathbuffer[MAX_HTTP_PATHLEN];
|
|
uint8_t is_dynamic:1;
|
|
uint16_t timeout;
|
|
|
|
union data_t
|
|
{
|
|
struct MFSFileInfo filedescriptor;
|
|
struct UserData { uint16_t a, b, c; } user;
|
|
struct UserDataPtr { void * v; } userptr;
|
|
} data;
|
|
|
|
void * rcb;
|
|
void * rcbDat; //For websockets primarily.
|
|
void * ccb; //Close callback (used for websockets, primarily)
|
|
|
|
uint32_t bytesleft;
|
|
uint32_t bytessofar;
|
|
|
|
uint8_t is404:1;
|
|
uint8_t isdone:1;
|
|
uint8_t isfirst:1;
|
|
uint8_t keep_alive:1;
|
|
uint8_t need_resend:1;
|
|
uint8_t send_pending:1; //If we can send data, we should?
|
|
uint8_t is_gzip:1;
|
|
|
|
int socket;
|
|
uint8_t corked_data[4096];
|
|
int corked_data_place;
|
|
};
|
|
|
|
extern struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS];
|
|
|
|
#endif
|
|
|
|
#ifndef _HTTP_BSD_H
|
|
#define _HTTP_BSD_H
|
|
|
|
|
|
//Call this to start your webserver.
|
|
int RunHTTP( int port );
|
|
int TickHTTP(); //returns -1 if problem.
|
|
|
|
//For running on a BSD Sockets System
|
|
int htsend( int socket, uint8_t * data, int datact );
|
|
void et_espconn_disconnect( int socket );
|
|
void http_recvcb(int whichhttp, char *pusrdata, unsigned short length);
|
|
void http_disconnetcb(int whichhttp);
|
|
int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure
|
|
void DataStartPacket();
|
|
extern uint8_t * databuff_ptr;
|
|
void PushBlob( const uint8_t * data, int len );
|
|
void PushByte( uint8_t c );
|
|
void PushString( const char * data );
|
|
int TCPCanSend( int socket, int size );
|
|
int TCPDoneSend( int socket );
|
|
int EndTCPWrite( int socket );
|
|
void TermHTTPServer();
|
|
|
|
extern int cork_binary_rx;
|
|
|
|
#endif
|
|
|
|
//Copyright 2012-2016 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or ColorChord License. You Choose.
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS];
|
|
|
|
#define HTDEBUG( x, ... ) printf( x, ##__VA_ARGS__ )
|
|
//#define HTDEBUG( x... )
|
|
|
|
//#define ISKEEPALIVE "keep-alive"
|
|
#define ISKEEPALIVE "close"
|
|
|
|
struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS];
|
|
struct HTTPConnection * curhttp;
|
|
uint8_t * curdata;
|
|
uint16_t curlen;
|
|
uint8_t wsmask[4];
|
|
uint8_t wsmaskplace;
|
|
|
|
|
|
void CloseEvent();
|
|
void InternalStartHTTP( );
|
|
void HTTPHandleInternalCallback( );
|
|
|
|
void HTTPClose( )
|
|
{
|
|
//This is dead code, but it is a testament to Charles.
|
|
//Do not do this here. Wait for the ESP to tell us the
|
|
//socket is successfully closed.
|
|
//curhttp->state = HTTP_STATE_NONE;
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
et_espconn_disconnect( curhttp->socket );
|
|
CloseEvent();
|
|
}
|
|
|
|
|
|
void HTTPGotData( )
|
|
{
|
|
uint8_t c;
|
|
curhttp->timeout = 0;
|
|
while( curlen-- )
|
|
{
|
|
c = HTTPPOP;
|
|
// sendhex2( h->state ); sendchr( ' ' );
|
|
|
|
switch( curhttp->state )
|
|
{
|
|
case HTTP_STATE_WAIT_METHOD:
|
|
if( c == ' ' )
|
|
{
|
|
curhttp->state = HTTP_STATE_WAIT_PATH;
|
|
curhttp->state_deets = 0;
|
|
}
|
|
break;
|
|
case HTTP_STATE_WAIT_PATH:
|
|
curhttp->pathbuffer[curhttp->state_deets++] = c;
|
|
if( curhttp->state_deets == MAX_HTTP_PATHLEN )
|
|
{
|
|
curhttp->state_deets--;
|
|
}
|
|
|
|
if( c == ' ' )
|
|
{
|
|
//Tricky: If we're a websocket, we need the whole header.
|
|
curhttp->pathbuffer[curhttp->state_deets-1] = 0;
|
|
curhttp->state_deets = 0;
|
|
|
|
if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws", 5 ) == 0 )
|
|
{
|
|
curhttp->state = HTTP_STATE_DATA_WEBSOCKET;
|
|
curhttp->state_deets = 0;
|
|
}
|
|
else
|
|
{
|
|
curhttp->state = HTTP_STATE_WAIT_PROTO;
|
|
}
|
|
}
|
|
break;
|
|
case HTTP_STATE_WAIT_PROTO:
|
|
if( c == '\n' )
|
|
{
|
|
curhttp->state = HTTP_STATE_WAIT_FLAG;
|
|
}
|
|
break;
|
|
case HTTP_STATE_WAIT_FLAG:
|
|
if( c == '\n' )
|
|
{
|
|
curhttp->state = HTTP_STATE_DATA_XFER;
|
|
InternalStartHTTP( );
|
|
}
|
|
else if( c != '\r' )
|
|
{
|
|
curhttp->state = HTTP_STATE_WAIT_INFLAG;
|
|
}
|
|
break;
|
|
case HTTP_STATE_WAIT_INFLAG:
|
|
if( c == '\n' )
|
|
{
|
|
curhttp->state = HTTP_STATE_WAIT_FLAG;
|
|
curhttp->state_deets = 0;
|
|
}
|
|
break;
|
|
case HTTP_STATE_DATA_XFER:
|
|
//Ignore any further data?
|
|
curlen = 0;
|
|
break;
|
|
case HTTP_STATE_DATA_WEBSOCKET:
|
|
WebSocketGotData( c );
|
|
break;
|
|
case HTTP_WAIT_CLOSE:
|
|
if( curhttp->keep_alive )
|
|
{
|
|
curhttp->state = HTTP_STATE_WAIT_METHOD;
|
|
}
|
|
else
|
|
{
|
|
HTTPClose( );
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void DoHTTP( uint8_t timed )
|
|
{
|
|
switch( curhttp->state )
|
|
{
|
|
case HTTP_STATE_NONE: //do nothing if no state.
|
|
curhttp->send_pending = 0;
|
|
break;
|
|
case HTTP_STATE_DATA_XFER:
|
|
curhttp->send_pending = 1;
|
|
if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend
|
|
{
|
|
if( curhttp->is_dynamic )
|
|
{
|
|
HTTPCustomCallback( );
|
|
}
|
|
else
|
|
{
|
|
HTTPHandleInternalCallback( );
|
|
}
|
|
}
|
|
break;
|
|
case HTTP_WAIT_CLOSE:
|
|
curhttp->send_pending = 0;
|
|
if( TCPDoneSend( curhttp->socket ) )
|
|
{
|
|
if( curhttp->keep_alive )
|
|
{
|
|
curhttp->state = HTTP_STATE_WAIT_METHOD;
|
|
}
|
|
else
|
|
{
|
|
HTTPClose( );
|
|
}
|
|
}
|
|
break;
|
|
case HTTP_STATE_DATA_WEBSOCKET:
|
|
curhttp->send_pending = 0;
|
|
if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend
|
|
{
|
|
WebSocketTickInternal();
|
|
}
|
|
break;
|
|
default:
|
|
if( timed )
|
|
{
|
|
if( curhttp->timeout++ > HTTP_SERVER_TIMEOUT )
|
|
{
|
|
HTTPClose( );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HTTPTick( uint8_t timed )
|
|
{
|
|
uint8_t i;
|
|
for( i = 0; i < HTTP_CONNECTIONS; i++ )
|
|
{
|
|
if( curhttp ) { HTDEBUG( "HTTPRXQ\n" ); break; }
|
|
curhttp = &HTTPConnections[i];
|
|
DoHTTP( timed );
|
|
curhttp = 0;
|
|
}
|
|
}
|
|
|
|
void HTTPHandleInternalCallback( )
|
|
{
|
|
uint16_t i, bytestoread;
|
|
|
|
if( curhttp->isdone )
|
|
{
|
|
HTTPClose( );
|
|
return;
|
|
}
|
|
if( curhttp->is404 )
|
|
{
|
|
DataStartPacket();
|
|
PushString("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\nFile not found.");
|
|
EndTCPWrite( curhttp->socket );
|
|
curhttp->isdone = 1;
|
|
return;
|
|
}
|
|
if( curhttp->isfirst )
|
|
{
|
|
char stto[10];
|
|
uint8_t slen = strlen( curhttp->pathbuffer );
|
|
const char * k;
|
|
|
|
DataStartPacket();;
|
|
//TODO: Content Length? MIME-Type?
|
|
PushString("HTTP/1.1 200 Ok\r\n");
|
|
|
|
#ifdef CUSTOM_HTTPHEADER_CODE
|
|
CUSTOM_HTTPHEADER_CODE
|
|
#endif
|
|
|
|
if( curhttp->bytesleft < 0xfffffffe )
|
|
{
|
|
PushString("Connection: "ISKEEPALIVE"\r\nContent-Length: ");
|
|
Uint32To10Str( stto, curhttp->bytesleft );
|
|
PushBlob( stto, strlen( stto ) );
|
|
curhttp->keep_alive = 1;
|
|
}
|
|
else
|
|
{
|
|
PushString("Connection: close");
|
|
curhttp->keep_alive = 0;
|
|
}
|
|
|
|
PushString( "\r\nContent-Type: " );
|
|
//Content-Type?
|
|
while( slen && ( curhttp->pathbuffer[--slen] != '.' ) );
|
|
k = &curhttp->pathbuffer[slen+1];
|
|
|
|
if( strcmp( k, "mp3" ) == 0 ) PushString( "audio/mpeg3" );
|
|
else if( strcmp( k, "jpg" ) == 0 ) PushString( "image/jpeg" );
|
|
else if( strcmp( k, "png" ) == 0 ) PushString( "image/png" );
|
|
else if( strcmp( k, "css" ) == 0 ) PushString( "text/css" );
|
|
else if( strcmp( k, "js" ) == 0 ) PushString( "text/javascript" );
|
|
else if( strcmp( k, "gz" ) == 0 ) PushString( "text/plain\r\nContent-Encoding: gzip\r\nCache-Control: public, max-age=3600" );
|
|
else if( curhttp->bytesleft == 0xfffffffe ) PushString( "text/plain" );
|
|
else PushString( "text/html" );
|
|
|
|
if( curhttp->is_gzip ) PushString( "\r\nContent-Encoding: gzip" );
|
|
|
|
PushString( "\r\n\r\n" );
|
|
EndTCPWrite( curhttp->socket );
|
|
curhttp->isfirst = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
DataStartPacket();
|
|
|
|
for( i = 0; i < 2 && curhttp->bytesleft; i++ )
|
|
{
|
|
int bpt = curhttp->bytesleft;
|
|
if( bpt > MFS_SECTOR ) bpt = MFS_SECTOR;
|
|
curhttp->bytesleft = MFSReadSector( databuff_ptr, &curhttp->data.filedescriptor );
|
|
databuff_ptr += bpt;
|
|
}
|
|
|
|
EndTCPWrite( curhttp->socket );
|
|
|
|
if( !curhttp->bytesleft )
|
|
curhttp->isdone = 1;
|
|
}
|
|
|
|
void InternalStartHTTP( )
|
|
{
|
|
int32_t clusterno;
|
|
int8_t i;
|
|
char * path = &curhttp->pathbuffer[0];
|
|
|
|
curhttp->is_gzip = 0;
|
|
|
|
if( curhttp->pathbuffer[0] == '/' )
|
|
path++;
|
|
|
|
if( path[0] == 'd' && path[1] == '/' )
|
|
{
|
|
curhttp->is_dynamic = 1;
|
|
curhttp->isfirst = 1;
|
|
curhttp->isdone = 0;
|
|
curhttp->is404 = 0;
|
|
HTTPCustomStart();
|
|
return;
|
|
}
|
|
|
|
if( !path[0] )
|
|
{
|
|
path = "index.html";
|
|
}
|
|
|
|
for( i = 0; path[i]; i++ )
|
|
if( path[i] == '?' ) path[i] = 0;
|
|
|
|
i = MFSOpenFile( path, &curhttp->data.filedescriptor );
|
|
|
|
curhttp->bytessofar = 0;
|
|
curhttp->is_gzip = 0;
|
|
|
|
if( i == MFS_FILE_COMPRESSED_MEMORY )
|
|
{
|
|
curhttp->is_gzip = 1;
|
|
}
|
|
else if( i < 0 )
|
|
{
|
|
HTDEBUG( "404(%s)\n", path );
|
|
curhttp->is404 = 1;
|
|
curhttp->isfirst = 1;
|
|
curhttp->isdone = 0;
|
|
curhttp->is_dynamic = 0;
|
|
curhttp->bytesleft = 0;
|
|
return;
|
|
}
|
|
|
|
curhttp->isfirst = 1;
|
|
curhttp->isdone = 0;
|
|
curhttp->is404 = 0;
|
|
curhttp->is_dynamic = 0;
|
|
curhttp->bytesleft = curhttp->data.filedescriptor.filelen;
|
|
}
|
|
|
|
|
|
void http_disconnetcb(int conn ) {
|
|
int r = conn;
|
|
if( r>=0 )
|
|
{
|
|
if( !HTTPConnections[r].is_dynamic ) MFSClose( &HTTPConnections[r].data.filedescriptor );
|
|
HTTPConnections[r].state = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void http_recvcb(int conn, char *pusrdata, unsigned short length)
|
|
{
|
|
int whichhttp = conn;
|
|
//Though it might be possible for this to interrupt the other
|
|
//tick task, I don't know if this is actually a probelem.
|
|
//I'm adding this back-up-the-register just in case.
|
|
if( curhttp ) { HTDEBUG( "Unexpected Race Condition\n" ); return; }
|
|
|
|
curhttp = &HTTPConnections[whichhttp];
|
|
curdata = (uint8_t*)pusrdata;
|
|
curlen = length;
|
|
HTTPGotData();
|
|
curhttp = 0 ;
|
|
|
|
}
|
|
|
|
int httpserver_connectcb( int socket )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < HTTP_CONNECTIONS; i++ )
|
|
{
|
|
if( HTTPConnections[i].state == 0 )
|
|
{
|
|
HTTPConnections[i].socket = socket;
|
|
HTTPConnections[i].state = HTTP_STATE_WAIT_METHOD;
|
|
break;
|
|
}
|
|
}
|
|
if( i == HTTP_CONNECTIONS )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
int URLDecode( char * decodeinto, int maxlen, const char * buf )
|
|
{
|
|
int i = 0;
|
|
|
|
for( ; buf && *buf; buf++ )
|
|
{
|
|
char c = *buf;
|
|
if( c == '+' )
|
|
{
|
|
decodeinto[i++] = ' ';
|
|
}
|
|
else if( c == '?' || c == '&' )
|
|
{
|
|
break;
|
|
}
|
|
else if( c == '%' )
|
|
{
|
|
if( *(buf+1) && *(buf+2) )
|
|
{
|
|
decodeinto[i++] = hex2byte( buf+1 );
|
|
buf += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
decodeinto[i++] = c;
|
|
}
|
|
if( i >= maxlen -1 ) break;
|
|
|
|
}
|
|
decodeinto[i] = 0;
|
|
return i;
|
|
}
|
|
|
|
|
|
#ifndef RD_SHA1_HASH_LEN
|
|
#define RD_SHA1_HASH_LEN RD_SHA1_DIGEST_SIZE
|
|
#endif
|
|
|
|
void WebSocketGotData( uint8_t c )
|
|
{
|
|
switch( curhttp->state_deets )
|
|
{
|
|
case 0:
|
|
{
|
|
int i = 0;
|
|
char inkey[120];
|
|
unsigned char hash[RD_SHA1_HASH_LEN];
|
|
RD_SHA1_CTX c;
|
|
int inkeylen = 0;
|
|
|
|
curhttp->is_dynamic = 1;
|
|
while( curlen > 20 )
|
|
{
|
|
curdata++; curlen--;
|
|
if( strncmp( curdata, "Sec-WebSocket-Key: ", 19 ) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( curlen <= 21 )
|
|
{
|
|
HTDEBUG( "No websocket key found.\n" );
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
return;
|
|
}
|
|
|
|
curdata+= 19;
|
|
curlen -= 19;
|
|
|
|
|
|
#define WS_KEY_LEN 36
|
|
#define WS_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
#define WS_RETKEY_SIZEM1 32
|
|
|
|
while( curlen > 1 )
|
|
{
|
|
uint8_t lc = *(curdata++);
|
|
inkey[i] = lc;
|
|
curlen--;
|
|
if( lc == '\r' )
|
|
{
|
|
inkey[i] = 0;
|
|
break;
|
|
}
|
|
i++;
|
|
if( i >= sizeof( inkey ) - WS_KEY_LEN - 5 )
|
|
{
|
|
HTDEBUG( "Websocket key too big.\n" );
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
return;
|
|
}
|
|
}
|
|
if( curlen <= 1 )
|
|
{
|
|
HTDEBUG( "Invalid websocket key found.\n" );
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
return;
|
|
}
|
|
|
|
if( i + WS_KEY_LEN + 1 >= sizeof( inkey ) )
|
|
{
|
|
HTDEBUG( "WSKEY Too Big.\n" );
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
return;
|
|
}
|
|
|
|
memcpy( &inkey[i], WS_KEY, WS_KEY_LEN + 1 );
|
|
i += WS_KEY_LEN;
|
|
RD_SHA1_Init( &c );
|
|
RD_SHA1_Update( &c, inkey, i );
|
|
RD_SHA1_Final( hash, &c );
|
|
|
|
#if (WS_RETKEY_SIZE > MAX_HTTP_PATHLEN - 10 )
|
|
#error MAX_HTTP_PATHLEN too short.
|
|
#endif
|
|
|
|
my_base64_encode( hash, RD_SHA1_HASH_LEN, curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) );
|
|
|
|
curhttp->bytessofar = 0;
|
|
curhttp->bytesleft = 0;
|
|
|
|
NewWebSocket();
|
|
//Respond...
|
|
curhttp->state_deets = 1;
|
|
break;
|
|
}
|
|
case 1:
|
|
if( c == '\n' ) curhttp->state_deets = 2;
|
|
break;
|
|
case 2:
|
|
if( c == '\r' ) curhttp->state_deets = 3;
|
|
else curhttp->state_deets = 1;
|
|
break;
|
|
case 3:
|
|
if( c == '\n' ) curhttp->state_deets = 4;
|
|
else curhttp->state_deets = 1;
|
|
break;
|
|
case 5: //Established connection.
|
|
{
|
|
//XXX TODO: Seems to malfunction on large-ish packets. I know it has problems with 140-byte payloads.
|
|
|
|
do
|
|
{
|
|
if( curlen < 5 ) //Can't interpret packet.
|
|
break;
|
|
|
|
uint8_t fin = c & 1;
|
|
uint8_t opcode = c << 4;
|
|
uint16_t payloadlen = *(curdata++);
|
|
|
|
curlen--;
|
|
if( !(payloadlen & 0x80) )
|
|
{
|
|
HTDEBUG( "Unmasked packet (%d)\n", payloadlen );
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
break;
|
|
}
|
|
|
|
if( opcode == 128 )
|
|
{
|
|
//Close connection.
|
|
//HTDEBUG( "CLOSE\n" );
|
|
//curhttp->state = HTTP_WAIT_CLOSE;
|
|
//break;
|
|
}
|
|
|
|
payloadlen &= 0x7f;
|
|
if( payloadlen == 127 )
|
|
{
|
|
//Very long payload.
|
|
//Not supported.
|
|
HTDEBUG( "Unsupported payload packet.\n" );
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
break;
|
|
}
|
|
else if( payloadlen == 126 )
|
|
{
|
|
payloadlen = (curdata[0] << 8) | curdata[1];
|
|
curdata += 2;
|
|
curlen -= 2;
|
|
}
|
|
wsmask[0] = curdata[0];
|
|
wsmask[1] = curdata[1];
|
|
wsmask[2] = curdata[2];
|
|
wsmask[3] = curdata[3];
|
|
curdata += 4;
|
|
curlen -= 4;
|
|
wsmaskplace = 0;
|
|
|
|
//XXX Warning: When packets get larger, they may split the
|
|
//websockets packets into multiple parts. We could handle this
|
|
//but at the cost of prescious RAM. I am chosing to just drop those
|
|
//packets on the floor, and restarting the connection.
|
|
if( curlen < payloadlen )
|
|
{
|
|
extern int cork_binary_rx;
|
|
cork_binary_rx = 1;
|
|
//HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen );
|
|
//curhttp->state = HTTP_WAIT_CLOSE;
|
|
HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen );
|
|
curhttp->state = HTTP_WAIT_CLOSE;
|
|
return;
|
|
}
|
|
|
|
char * newcurdata = curdata + payloadlen + 1;
|
|
WebSocketData( payloadlen );
|
|
curlen -= payloadlen;
|
|
curdata = newcurdata;
|
|
}
|
|
while( curlen > 5 );
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WebSocketTickInternal()
|
|
{
|
|
switch( curhttp->state_deets )
|
|
{
|
|
case 4: //Has key full HTTP header, etc. wants response.
|
|
DataStartPacket();;
|
|
PushString( "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " );
|
|
PushString( curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) );
|
|
PushString( "\r\n\r\n" );
|
|
EndTCPWrite( curhttp->socket );
|
|
curhttp->state_deets = 5;
|
|
curhttp->keep_alive = 0;
|
|
break;
|
|
case 5:
|
|
WebSocketTick();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WebSocketSend( uint8_t * data, int size )
|
|
{
|
|
DataStartPacket();;
|
|
PushByte( 0x82 ); //0x81 is text.
|
|
if( size >= 126 )
|
|
{
|
|
PushByte( 0x00 | 126 );
|
|
PushByte( size>>8 );
|
|
PushByte( size&0xff );
|
|
}
|
|
else
|
|
{
|
|
PushByte( 0x00 | size );
|
|
}
|
|
PushBlob( data, size );
|
|
EndTCPWrite( curhttp->socket );
|
|
curhttp->send_pending = 1;
|
|
}
|
|
|
|
uint8_t WSPOPMASK()
|
|
{
|
|
uint8_t mask = wsmask[wsmaskplace];
|
|
wsmaskplace = (wsmaskplace+1)&3;
|
|
return (*curdata++)^(mask);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
#include <winsock2.h>
|
|
#define socklen_t uint32_t
|
|
#define SHUT_RDWR SD_BOTH
|
|
#define MSG_NOSIGNAL 0
|
|
#else
|
|
#define closesocket close
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <linux/in.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
uint16_t htons(uint16_t hostshort);
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
static int serverSocket;
|
|
|
|
uint8_t * databuff_ptr;
|
|
uint8_t databuff[1536];
|
|
int cork_binary_rx;
|
|
|
|
|
|
#ifndef HTTP_POLL_TIMEOUT
|
|
#define HTTP_POLL_TIMEOUT 0 //This is a little weird, it's an adaptation from cnhttp.
|
|
#endif
|
|
|
|
|
|
#define DESTROY_SOCKETS_LIST 200
|
|
int destroy_sockets[DESTROY_SOCKETS_LIST];
|
|
int destroy_socket_head = 0;
|
|
int sockets[HTTP_CONNECTIONS];
|
|
|
|
void et_espconn_disconnect( int socket )
|
|
{
|
|
shutdown( socket, SHUT_RDWR );
|
|
int i;
|
|
//printf( "Shut: %d\n", socket );
|
|
for( i = 0; i < HTTP_CONNECTIONS; i++ )
|
|
{
|
|
if( sockets[i] == socket )
|
|
{
|
|
http_disconnetcb( i );
|
|
sockets[i] = 0;
|
|
}
|
|
}
|
|
|
|
if( destroy_sockets[destroy_socket_head] ) closesocket( destroy_sockets[destroy_socket_head] );
|
|
destroy_sockets[destroy_socket_head] = socket;
|
|
destroy_socket_head = (destroy_socket_head+1)%DESTROY_SOCKETS_LIST;
|
|
|
|
}
|
|
|
|
void DataStartPacket()
|
|
{
|
|
databuff_ptr = databuff;
|
|
}
|
|
|
|
void PushByte( uint8_t c )
|
|
{
|
|
if( databuff_ptr - databuff + 1 >= sizeof( databuff ) ) return;
|
|
*(databuff_ptr++) = c;
|
|
}
|
|
|
|
void PushBlob( const uint8_t * data, int len )
|
|
{
|
|
if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return;
|
|
memcpy( databuff_ptr, data, len );
|
|
databuff_ptr += len;
|
|
}
|
|
|
|
void PushString( const char * data )
|
|
{
|
|
int len = strlen( data );
|
|
if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return;
|
|
memcpy( databuff_ptr, data, len );
|
|
databuff_ptr += len;
|
|
}
|
|
|
|
int TCPCanSend( int socket, int size )
|
|
{
|
|
fd_set write_fd_set;
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
FD_ZERO (&write_fd_set);
|
|
FD_SET (socket, &write_fd_set);
|
|
|
|
int r = select (FD_SETSIZE, NULL, &write_fd_set, NULL, &tv);
|
|
if (r < 0)
|
|
{
|
|
perror ("select");
|
|
return -1;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
int TCPCanRead( int sock )
|
|
{
|
|
fd_set read_fd_set;
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
FD_ZERO (&read_fd_set);
|
|
FD_SET (sock, &read_fd_set);
|
|
|
|
int r;
|
|
r = select (FD_SETSIZE, &read_fd_set, NULL, NULL, &tv);
|
|
if (r < 0)
|
|
{
|
|
perror ("select");
|
|
return -1;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
int TCPException( int sock )
|
|
{
|
|
int error_code;
|
|
int error_code_size = sizeof(error_code);
|
|
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error_code, &error_code_size);
|
|
if( error_code >= 0 ) return 0;
|
|
else return 1;
|
|
}
|
|
|
|
int TCPDoneSend( int socket )
|
|
{
|
|
return TCPCanSend( socket, 1 );
|
|
}
|
|
|
|
|
|
int EndTCPWrite( int socket )
|
|
{
|
|
int r = send( socket, databuff, databuff_ptr-databuff, MSG_NOSIGNAL );
|
|
databuff_ptr = databuff;
|
|
return r;
|
|
}
|
|
|
|
void TermHTTPServer()
|
|
{
|
|
shutdown( serverSocket, SHUT_RDWR );
|
|
}
|
|
|
|
int TickHTTP()
|
|
{
|
|
int i;
|
|
//struct pollfd allpolls[HTTP_CONNECTIONS+1];
|
|
short mappedhttp[HTTP_CONNECTIONS+1];
|
|
if( serverSocket == 0 ) return -1;
|
|
|
|
do
|
|
{
|
|
static double last;
|
|
double now;
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
static LARGE_INTEGER lpf;
|
|
LARGE_INTEGER li;
|
|
|
|
if( !lpf.QuadPart )
|
|
{
|
|
QueryPerformanceFrequency( &lpf );
|
|
}
|
|
|
|
QueryPerformanceCounter( &li );
|
|
now = (double)li.QuadPart / (double)lpf.QuadPart;
|
|
#else
|
|
struct timeval tv;
|
|
gettimeofday( &tv, 0 );
|
|
now = ((double)tv.tv_usec)/1000000. + (tv.tv_sec);
|
|
#endif
|
|
double dl = now - last;
|
|
if( dl > .1 )
|
|
{
|
|
int i;
|
|
HTTPTick( 1 );
|
|
last = now;
|
|
}
|
|
else
|
|
{
|
|
HTTPTick( 0 );
|
|
}
|
|
|
|
/* int pollct = 1;
|
|
allpolls[0].fd = serverSocket;
|
|
allpolls[0].events = LISTENPOLL;
|
|
for( i = 0; i < HTTP_CONNECTIONS;i ++)
|
|
{
|
|
if( !sockets[i] || HTTPConnections[i].state == 0 ) continue;
|
|
allpolls[pollct].fd = sockets[i];
|
|
allpolls[pollct].events = POLLIN | (HTTPConnections[i].send_pending?POLLOUT:0);
|
|
mappedhttp[pollct] = i;
|
|
pollct++;
|
|
}
|
|
|
|
//Do something to watch all currently-waiting sockets.
|
|
poll( allpolls, pollct, HTTP_POLL_TIMEOUT );
|
|
*/
|
|
//If there's faults, bail.
|
|
if( TCPException( serverSocket ) )
|
|
{
|
|
closesocket( serverSocket );
|
|
for( i = 0; i < HTTP_CONNECTIONS;i ++)
|
|
{
|
|
if( sockets[i] ) closesocket( sockets[i] );
|
|
}
|
|
break;
|
|
}
|
|
if( TCPCanRead( serverSocket ) )
|
|
{
|
|
struct sockaddr_in tin;
|
|
socklen_t addrlen = sizeof(tin);
|
|
memset( &tin, 0, addrlen );
|
|
int tsocket = accept( serverSocket, (struct sockaddr *)&tin, &addrlen );
|
|
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
struct linger lx;
|
|
lx.l_onoff = 1;
|
|
lx.l_linger = 0;
|
|
|
|
//Disable the linger here, too.
|
|
setsockopt( tsocket, SOL_SOCKET, SO_LINGER, (const char*)&lx, sizeof( lx ) );
|
|
#else
|
|
struct linger lx;
|
|
lx.l_onoff = 1;
|
|
lx.l_linger = 0;
|
|
|
|
//Disable the linger here, too.
|
|
setsockopt( tsocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) );
|
|
#endif
|
|
|
|
int r = httpserver_connectcb( tsocket );
|
|
|
|
if( r == -1 )
|
|
{
|
|
closesocket( tsocket );
|
|
}
|
|
else
|
|
{
|
|
sockets[r] = tsocket;
|
|
}
|
|
}
|
|
for( i = 0; i < HTTP_CONNECTIONS; i++)
|
|
{
|
|
int wc = i;
|
|
if( !sockets[i] || HTTPConnections[i].state == 0 ) continue;
|
|
if( TCPException(sockets[i]) )
|
|
{
|
|
http_disconnetcb( wc );
|
|
closesocket( sockets[wc] );
|
|
sockets[wc] = 0;
|
|
}
|
|
else if( TCPCanRead( sockets[i] ) )
|
|
{
|
|
int dco = HTTPConnections[i].corked_data_place;
|
|
uint8_t data[8192];
|
|
memcpy( data, HTTPConnections[i].corked_data, dco );
|
|
int len = recv( sockets[wc], data+dco, 8192-dco, 0 );
|
|
if( len )
|
|
{
|
|
cork_binary_rx = 0;
|
|
http_recvcb( wc, data, len+dco );
|
|
if( cork_binary_rx )
|
|
{
|
|
int to_cork = len;
|
|
if( to_cork > sizeof( HTTPConnections[i].corked_data ) + HTTPConnections[i].corked_data_place )
|
|
{
|
|
http_disconnetcb( wc );
|
|
closesocket ( sockets[wc] );
|
|
sockets[wc] = 0;
|
|
fprintf( stderr, "Error: too much data to buffer on websocket\n" );
|
|
}
|
|
else
|
|
{
|
|
memcpy( HTTPConnections[i].corked_data + dco, data + dco, to_cork );
|
|
HTTPConnections[i].corked_data_place += to_cork;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HTTPConnections[i].corked_data_place = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
http_disconnetcb( wc );
|
|
closesocket( sockets[wc] );
|
|
sockets[wc] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if HTTP_POLL_TIMEOUT != 0
|
|
while(1);
|
|
#else
|
|
while(0);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int RunHTTP( int port )
|
|
{
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
{
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
int err;
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
|
|
err = WSAStartup(wVersionRequested, &wsaData);
|
|
if (err != 0) {
|
|
/* Tell the user that we could not find a usable */
|
|
/* Winsock DLL. */
|
|
fprintf( stderr, "WSAStartup failed with error: %d\n", err);
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
int acceptfaults = 0;
|
|
struct sockaddr_in sin;
|
|
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
//Make sure the socket worked.
|
|
if( serverSocket == -1 )
|
|
{
|
|
fprintf( stderr, "Error: Cannot create socket.\n" );
|
|
return -1;
|
|
}
|
|
|
|
//Disable SO_LINGER (Well, enable it but turn it way down)
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
struct linger lx;
|
|
lx.l_onoff = 1;
|
|
lx.l_linger = 0;
|
|
setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) );
|
|
|
|
//Enable SO_REUSEADDR
|
|
int reusevar = 1;
|
|
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar));
|
|
#else
|
|
struct linger lx;
|
|
lx.l_onoff = 1;
|
|
lx.l_linger = 0;
|
|
setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) );
|
|
|
|
//Enable SO_REUSEADDR
|
|
int reusevar = 1;
|
|
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar));
|
|
#endif
|
|
//Setup socket for listening address.
|
|
memset( &sin, 0, sizeof( sin ) );
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr.s_addr = INADDR_ANY;
|
|
sin.sin_port = htons( port );
|
|
|
|
//Actually bind to the socket
|
|
if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 )
|
|
{
|
|
fprintf( stderr, "Could not bind to socket: %d\n", port );
|
|
closesocket( serverSocket );
|
|
serverSocket = 0;
|
|
return -1;
|
|
}
|
|
|
|
//Finally listen.
|
|
if( listen( serverSocket, 5 ) == -1 )
|
|
{
|
|
fprintf(stderr, "Could not lieten to socket.");
|
|
closesocket( serverSocket );
|
|
serverSocket = 0;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Uint32To10Str( char * out, uint32_t dat )
|
|
{
|
|
int tens = 1000000000;
|
|
int val;
|
|
int place = 0;
|
|
|
|
while( tens > 1 )
|
|
{
|
|
if( dat/tens ) break;
|
|
tens/=10;
|
|
}
|
|
|
|
while( tens )
|
|
{
|
|
val = dat/tens;
|
|
dat -= val*tens;
|
|
tens /= 10;
|
|
out[place++] = val + '0';
|
|
}
|
|
|
|
out[place] = 0;
|
|
}
|
|
|
|
//from http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
|
|
static const char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
|
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
|
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
|
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
|
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
|
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
|
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
|
'4', '5', '6', '7', '8', '9', '+', '/'};
|
|
|
|
static const int mod_table[] = {0, 2, 1};
|
|
|
|
void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data )
|
|
{
|
|
|
|
int i, j;
|
|
int output_length = 4 * ((input_length + 2) / 3);
|
|
|
|
if( !encoded_data ) return;
|
|
if( !data ) { encoded_data[0] = '='; encoded_data[1] = 0; return; }
|
|
|
|
for (i = 0, j = 0; i < input_length; ) {
|
|
|
|
uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
|
|
uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
|
|
uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
|
|
|
|
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
|
|
|
|
encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
|
|
encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
|
|
encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
|
|
encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
|
|
}
|
|
|
|
for (i = 0; i < mod_table[input_length % 3]; i++)
|
|
encoded_data[output_length - 1 - i] = '=';
|
|
|
|
encoded_data[j] = 0;
|
|
}
|
|
|
|
uint8_t hex1byte( char c )
|
|
{
|
|
if( c >= '0' && c <= '9' ) return c - '0';
|
|
if( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
|
|
if( c >= 'A' && c <= 'F' ) return c - 'A' + 10;
|
|
return 0;
|
|
}
|
|
|
|
uint8_t hex2byte( const char * c )
|
|
{
|
|
return (hex1byte(c[0])<<4) | (hex1byte(c[1]));
|
|
}
|
|
|
|
#include <string.h>
|
|
|
|
#ifdef USE_RAM_MFS
|
|
|
|
unsigned char webpage_buffer[] = {
|
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xbd, 0x3b, 0x6b, 0x73, 0xda, 0xc8, 0xb2, 0x9f, 0xe1, 0x57, 0x4c, 0xb8, 0x75, 0x37, 0x60, 0x83, 0xc4, 0xc3, 0x76, 0xbc, 0xc6, 0x76,
|
|
0x15, 0xb6, 0x71, 0xe2, 0x73, 0x6c, 0xc7, 0xd7, 0xe0, 0x4d, 0x52, 0xa9, 0x14, 0x25, 0xa4, 0x01, 0x69, 0x2d, 0x24, 0x56, 0x0f, 0x03, 0xd9, 0xf8, 0xbf, 0x9f, 0xee, 0x9e, 0xd1, 0x13, 0x61, 0x9c,
|
|
0x3d, 0xbb, 0x97, 0xc4, 0x20, 0xcd, 0xf4, 0xf4, 0xf4, 0x6b, 0xfa, 0x31, 0x1a, 0xb1, 0xe3, 0x37, 0x17, 0x1f, 0xcf, 0x87, 0x5f, 0xee, 0xfa, 0xcc, 0x0c, 0x66, 0xf6, 0x69, 0xf9, 0x58, 0xfc, 0x94,
|
|
0x8e, 0x4d, 0xae, 0x19, 0xf0, 0x5b, 0x3a, 0x9e, 0xf1, 0x40, 0x63, 0xba, 0xa9, 0x79, 0x3e, 0x0f, 0x4e, 0x2a, 0x0f, 0xc3, 0xcb, 0xc6, 0x61, 0x85, 0x3a, 0x02, 0x2b, 0xb0, 0xf9, 0xa9, 0xee, 0x98,
|
|
0x41, 0x30, 0x3f, 0x56, 0xc5, 0x1d, 0xb6, 0xfb, 0xc1, 0x4a, 0x5c, 0x95, 0xc6, 0xae, 0xb1, 0x62, 0x7f, 0xb2, 0xb1, 0xa6, 0x3f, 0x4e, 0x3d, 0x37, 0x74, 0x8c, 0x86, 0xee, 0xda, 0xae, 0x77, 0xc4,
|
|
0xfe, 0xa7, 0x09, 0x9f, 0xc3, 0x66, 0x97, 0x3d, 0x23, 0x98, 0xe1, 0xea, 0xe1, 0x8c, 0x3b, 0x01, 0x3b, 0x62, 0x7f, 0xba, 0x4f, 0xdc, 0x9b, 0xd8, 0xee, 0xe2, 0x88, 0x99, 0x96, 0x61, 0x70, 0xa7,
|
|
0xcb, 0x16, 0x96, 0x11, 0x98, 0x27, 0xad, 0x66, 0xf3, 0x7f, 0xbb, 0xcc, 0xe4, 0xd6, 0xd4, 0x0c, 0xc4, 0x0d, 0x0e, 0x3d, 0x56, 0xa3, 0xc9, 0x8e, 0x55, 0x49, 0xf0, 0x31, 0x4d, 0x4a, 0xcd, 0x27,
|
|
0x15, 0x01, 0x7f, 0x44, 0xf0, 0x33, 0xcd, 0x9b, 0x5a, 0xce, 0x11, 0x4c, 0x3a, 0xd7, 0x0c, 0xc3, 0x72, 0xa6, 0x47, 0x4d, 0xc1, 0x87, 0x61, 0x3d, 0xb1, 0xc1, 0xf0, 0xcb, 0x75, 0xff, 0xa4, 0x32,
|
|
0x77, 0x7d, 0x2b, 0x70, 0x9d, 0x23, 0x6d, 0xec, 0xbb, 0x76, 0x18, 0xf0, 0xee, 0xf7, 0x86, 0xe5, 0x18, 0x7c, 0x79, 0xd4, 0x21, 0xd0, 0xd2, 0xb1, 0xae, 0x39, 0x4f, 0x9a, 0x2f, 0x69, 0xda, 0x6f,
|
|
0x36, 0x23, 0x8a, 0xf0, 0xd2, 0x32, 0x4e, 0x44, 0xf7, 0xe9, 0xb1, 0x2a, 0x2f, 0x88, 0x44, 0xc0, 0x5f, 0x38, 0x8f, 0x95, 0x9e, 0x68, 0xec, 0x06, 0x81, 0x3b, 0x03, 0xea, 0x08, 0xf5, 0xd1, 0xc1,
|
|
0x5e, 0x33, 0x9e, 0xbb, 0xd5, 0xec, 0x0a, 0xb1, 0x2d, 0x4c, 0x0b, 0x20, 0xb5, 0x23, 0x8f, 0x1b, 0x5d, 0xc9, 0xd9, 0x7e, 0x73, 0xbe, 0xac, 0x9c, 0x1e, 0x5b, 0xce, 0x3c, 0x0c, 0x58, 0xb0, 0x9a,
|
|
0xf3, 0x13, 0x3f, 0x1c, 0xcf, 0xac, 0x80, 0x3d, 0x69, 0x76, 0x08, 0x02, 0x98, 0xf8, 0x15, 0xe6, 0x3a, 0xba, 0x6d, 0xe9, 0x8f, 0x27, 0x95, 0xa1, 0x3b, 0x9d, 0xda, 0xfc, 0x72, 0x50, 0xad, 0x75,
|
|
0x61, 0x0c, 0x92, 0x03, 0x24, 0xfb, 0x81, 0x16, 0x84, 0x48, 0x32, 0x92, 0x99, 0x10, 0xeb, 0xeb, 0x9e, 0x35, 0x0f, 0x4e, 0xcb, 0x36, 0x0f, 0x98, 0xc7, 0x81, 0x0e, 0x0f, 0x24, 0xc6, 0x4e, 0xd8,
|
|
0x44, 0xb3, 0x7d, 0xde, 0xa5, 0xe6, 0x49, 0x68, 0xdb, 0x00, 0xc6, 0xb9, 0xb3, 0xa9, 0x1d, 0x95, 0xe9, 0x59, 0x06, 0x87, 0x7e, 0x07, 0x1a, 0xbb, 0xe5, 0x49, 0xe8, 0xe8, 0xc8, 0x37, 0x4b, 0x48,
|
|
0x01, 0xfb, 0x28, 0x1c, 0xf0, 0x66, 0xbd, 0x15, 0xcd, 0xa5, 0xac, 0xaa, 0xe7, 0xae, 0x33, 0xb1, 0xa6, 0xa1, 0xc7, 0xd9, 0x27, 0x3e, 0x7e, 0x7f, 0xcd, 0x06, 0x41, 0x38, 0x99, 0xb0, 0xaa, 0x66,
|
|
0x83, 0xd9, 0xb0, 0xc0, 0x65, 0x63, 0x0e, 0x1a, 0xf6, 0x02, 0xe6, 0x4e, 0xd8, 0xd4, 0x76, 0xc7, 0x9a, 0xcd, 0x74, 0xd7, 0x09, 0xf8, 0x32, 0xa8, 0x11, 0x79, 0x52, 0x85, 0x27, 0x2c, 0x32, 0x3b,
|
|
0x65, 0xca, 0x83, 0xbe, 0xcd, 0xf1, 0xf2, 0x6c, 0x75, 0x65, 0x54, 0xdf, 0x0a, 0x88, 0xb7, 0x35, 0xc1, 0xce, 0x62, 0x6a, 0x03, 0xb0, 0x68, 0x43, 0xd0, 0x73, 0x81, 0xac, 0xfa, 0x76, 0xc1, 0xc7,
|
|
0x53, 0x1b, 0xa1, 0xac, 0x49, 0x95, 0xbd, 0x41, 0xb0, 0x5a, 0xf9, 0xcf, 0x72, 0x49, 0x55, 0xff, 0xa5, 0x39, 0x8f, 0x2b, 0xd6, 0x00, 0xc1, 0xb3, 0x4b, 0xcb, 0xe3, 0x13, 0x77, 0xc9, 0x0e, 0x3b,
|
|
0x75, 0x30, 0x9a, 0xc0, 0x64, 0xb7, 0xbf, 0x5d, 0x5d, 0x5c, 0xf5, 0xd8, 0xfb, 0xbb, 0x87, 0x3a, 0x5b, 0xb9, 0x21, 0x73, 0x38, 0x37, 0x90, 0x68, 0xcd, 0x7f, 0x64, 0xc1, 0xc2, 0xd2, 0xb9, 0x52,
|
|
0x2e, 0x6d, 0x9f, 0xf1, 0x39, 0xa2, 0x6c, 0x60, 0x6a, 0xa0, 0x9a, 0x48, 0xbe, 0x4c, 0x55, 0x07, 0x81, 0xe6, 0x18, 0x9a, 0x67, 0xb0, 0x89, 0xad, 0x01, 0xaf, 0x68, 0x37, 0xcc, 0x27, 0xa0, 0x68,
|
|
0x48, 0xef, 0xec, 0xb7, 0x18, 0x1e, 0x3e, 0xaa, 0xda, 0xf3, 0x3c, 0x6d, 0xc5, 0xc6, 0x20, 0x44, 0xc0, 0x34, 0x01, 0x78, 0x10, 0x77, 0x00, 0x94, 0xf8, 0xc9, 0x88, 0xf3, 0x2d, 0x23, 0x68, 0x1e,
|
|
0x5f, 0x89, 0x06, 0x3c, 0x7c, 0xbe, 0xbc, 0xbf, 0x49, 0x86, 0xa8, 0xea, 0x83, 0x63, 0x01, 0xd8, 0x8c, 0xd9, 0xae, 0xae, 0x91, 0xfe, 0x71, 0x50, 0xe0, 0x69, 0x8e, 0x4f, 0xcd, 0xd0, 0x00, 0xcb,
|
|
0xc0, 0x32, 0x24, 0x1e, 0x54, 0xf2, 0x43, 0x60, 0xd9, 0x56, 0x80, 0x0b, 0x19, 0x95, 0x8b, 0xe0, 0x42, 0xd9, 0xbe, 0x66, 0x22, 0xc3, 0x60, 0x17, 0x84, 0x48, 0x49, 0x4c, 0x0a, 0xe6, 0x1d, 0xcd,
|
|
0xb4, 0x47, 0x2e, 0x44, 0x52, 0x25, 0x2e, 0x86, 0x20, 0xb6, 0x3a, 0x9b, 0x78, 0xda, 0x14, 0xaf, 0x84, 0x7e, 0x90, 0x46, 0xec, 0x03, 0xfa, 0x60, 0x88, 0x42, 0x98, 0xa2, 0x41, 0xd8, 0xf0, 0x5b,
|
|
0xff, 0x7e, 0xd8, 0xff, 0x3c, 0x1a, 0x7c, 0xe8, 0x5d, 0xf4, 0xef, 0x41, 0xd6, 0xa8, 0x0e, 0x45, 0x88, 0x70, 0xe0, 0x86, 0x9e, 0xce, 0xab, 0x38, 0xba, 0x1e, 0xe3, 0x67, 0x11, 0x8c, 0xee, 0xce,
|
|
0xe6, 0x96, 0x1d, 0xa1, 0xc2, 0x6e, 0xec, 0xb1, 0xc0, 0x34, 0xd1, 0x38, 0x50, 0x91, 0xa2, 0xeb, 0x4e, 0xf3, 0x34, 0x70, 0xaa, 0x12, 0xa6, 0x4e, 0x54, 0x9c, 0x7f, 0xbc, 0xb9, 0xbb, 0xba, 0xee,
|
|
0x8f, 0x06, 0xc3, 0xde, 0xf0, 0x61, 0x50, 0x83, 0x55, 0x81, 0xee, 0x46, 0xb3, 0x01, 0xa0, 0x9a, 0x19, 0x7c, 0xe5, 0x4c, 0xdc, 0x6b, 0x77, 0x2a, 0xd0, 0x23, 0x7e, 0x58, 0x12, 0xc4, 0x12, 0x32,
|
|
0xb9, 0x89, 0xa5, 0xcb, 0xfb, 0xde, 0xfb, 0x9b, 0xfe, 0xed, 0xf0, 0x25, 0xa6, 0x70, 0x7c, 0x5a, 0x54, 0x85, 0x4c, 0x61, 0xf7, 0x36, 0xa6, 0x04, 0xa2, 0xbf, 0xc4, 0x14, 0xa1, 0x17, 0x4c, 0x95,
|
|
0x84, 0xe3, 0xc9, 0x6a, 0xe9, 0xce, 0x73, 0xa7, 0x30, 0x4d, 0x35, 0x22, 0x4e, 0x0b, 0x02, 0x4d, 0x37, 0x25, 0x6d, 0x00, 0x2c, 0xe8, 0xdf, 0xdc, 0x1b, 0xe9, 0x04, 0x7b, 0x6d, 0xcb, 0x79, 0x8c,
|
|
0xf0, 0x41, 0x67, 0xd4, 0x3c, 0x06, 0x9f, 0xdb, 0x0b, 0x02, 0xcf, 0x1a, 0x5f, 0x4b, 0x5b, 0xad, 0x32, 0x1a, 0xdb, 0xac, 0xb3, 0x8a, 0xd6, 0xac, 0xb0, 0xad, 0x80, 0x2d, 0x04, 0x6c, 0x09, 0x40,
|
|
0x68, 0x08, 0x3d, 0x07, 0xdb, 0x71, 0xcd, 0x96, 0x85, 0x7b, 0xf8, 0xc4, 0x61, 0x1d, 0x68, 0xb0, 0xea, 0x17, 0xae, 0x5c, 0x9a, 0x7e, 0x1d, 0x96, 0x00, 0x67, 0x96, 0xcf, 0x34, 0xb1, 0x10, 0x1a,
|
|
0xe9, 0x85, 0x5b, 0x27, 0xf3, 0x9f, 0xb9, 0x3e, 0x08, 0x44, 0x5b, 0x18, 0xf0, 0xc7, 0xdc, 0xf1, 0xef, 0x5c, 0x0f, 0x7c, 0xe1, 0x2b, 0x62, 0x1f, 0x90, 0x5f, 0x01, 0x20, 0xed, 0x4a, 0x28, 0x57,
|
|
0xde, 0x13, 0xd7, 0xf7, 0xd8, 0x72, 0xe2, 0xcd, 0xba, 0x4c, 0x23, 0xaa, 0x21, 0xe8, 0x60, 0x63, 0x87, 0x69, 0xcd, 0x5c, 0xd3, 0x1e, 0xd3, 0x5a, 0x5d, 0x08, 0x20, 0xde, 0x0a, 0x5d, 0x3e, 0x35,
|
|
0x3c, 0xe9, 0xd0, 0xe0, 0xc2, 0x02, 0x9d, 0x69, 0x96, 0x43, 0x3e, 0x1b, 0xa6, 0xba, 0x93, 0x41, 0x0c, 0x66, 0x46, 0xa0, 0x2a, 0x60, 0x52, 0x96, 0xab, 0x1d, 0x9c, 0x04, 0x7e, 0x77, 0xe9, 0xf7,
|
|
0xfb, 0xa2, 0x8e, 0xcd, 0xdf, 0x41, 0x7e, 0xca, 0x3e, 0x88, 0x04, 0x30, 0x01, 0x38, 0xe2, 0x7f, 0xae, 0xd4, 0x91, 0xbe, 0xb9, 0xc7, 0x75, 0xcb, 0x47, 0x2c, 0x33, 0x6e, 0x58, 0xe1, 0x6c, 0x0e,
|
|
0x8e, 0xcb, 0xd5, 0x82, 0xd7, 0xcc, 0x7f, 0x09, 0xba, 0x3e, 0x27, 0x39, 0x49, 0x02, 0x9e, 0x74, 0x98, 0xf7, 0xfb, 0xa2, 0x86, 0xc8, 0x51, 0xfc, 0x24, 0x9d, 0xc8, 0x17, 0x49, 0x83, 0x93, 0x9e,
|
|
0x28, 0xd6, 0x59, 0x2c, 0x3f, 0xd0, 0x1a, 0x52, 0x2c, 0x07, 0x62, 0x94, 0x21, 0xc3, 0x67, 0x81, 0xc9, 0x23, 0x2d, 0x09, 0x71, 0x2b, 0xa1, 0x1f, 0x1b, 0x62, 0x3c, 0x5a, 0x0e, 0x7a, 0x0f, 0x06,
|
|
0xeb, 0xbb, 0x33, 0x4e, 0x96, 0xc6, 0x97, 0xaa, 0x50, 0xa3, 0x70, 0x94, 0xa0, 0x64, 0xf0, 0xf3, 0x18, 0xa7, 0xa7, 0x1c, 0x40, 0x02, 0x6f, 0xc5, 0x2c, 0x47, 0xa0, 0x14, 0x2e, 0x39, 0xb1, 0xf3,
|
|
0x33, 0x1a, 0x10, 0x99, 0xb9, 0x70, 0xbf, 0x45, 0xbd, 0xc2, 0x9a, 0xde, 0x42, 0x24, 0x0c, 0x7d, 0x94, 0x15, 0x5a, 0x54, 0x3c, 0x99, 0xef, 0xb2, 0xdf, 0x43, 0xb0, 0x19, 0xee, 0x68, 0x63, 0xc1,
|
|
0xc7, 0xac, 0xce, 0x1c, 0x77, 0x21, 0xb9, 0x10, 0xcd, 0xbf, 0x11, 0x9d, 0xc2, 0x8e, 0xc9, 0xab, 0x57, 0x9b, 0x91, 0x81, 0x6f, 0x02, 0x68, 0xc9, 0x89, 0xfb, 0x02, 0xaf, 0x66, 0xcf, 0x4d, 0x8d,
|
|
0xc1, 0x95, 0x83, 0x39, 0x55, 0x7a, 0x28, 0xad, 0xf1, 0xb3, 0xeb, 0xfe, 0xed, 0x45, 0xbc, 0x66, 0x10, 0xea, 0x12, 0x1c, 0x36, 0x75, 0x0d, 0xee, 0xcf, 0x47, 0xbd, 0xeb, 0xbb, 0x0f, 0x3d, 0xe1,
|
|
0x2d, 0x3e, 0xde, 0xf6, 0x47, 0x37, 0x57, 0xb7, 0x0f, 0x83, 0x51, 0xdc, 0x81, 0xe3, 0x20, 0x76, 0xc3, 0x92, 0xe0, 0x8a, 0x0d, 0xfe, 0x81, 0x62, 0x31, 0x86, 0x3f, 0x55, 0xbd, 0x70, 0x19, 0x45,
|
|
0x43, 0xb6, 0x70, 0x3d, 0x88, 0x9c, 0x26, 0x04, 0xbb, 0x19, 0x72, 0x6b, 0x6a, 0xf3, 0x39, 0x24, 0x24, 0x1c, 0x14, 0xb0, 0x42, 0x6f, 0x30, 0x83, 0x80, 0x8a, 0xf1, 0x44, 0x08, 0x0d, 0x62, 0x6d,
|
|
0x2c, 0x7c, 0x7f, 0xe5, 0x07, 0x7c, 0xa6, 0x40, 0xdf, 0xd0, 0x84, 0x95, 0x67, 0x42, 0xdc, 0xb4, 0xb9, 0x2f, 0xe5, 0x87, 0xd2, 0x04, 0xae, 0x80, 0x54, 0x13, 0xf3, 0x08, 0x70, 0x16, 0xdc, 0x57,
|
|
0x7d, 0x3e, 0xc5, 0x24, 0x01, 0x64, 0x0b, 0xb0, 0x91, 0xc8, 0x41, 0xac, 0x20, 0x03, 0x1b, 0x96, 0x10, 0x26, 0x5a, 0x30, 0x9b, 0xc8, 0x39, 0x9e, 0xe2, 0x9c, 0x27, 0x89, 0x50, 0xe7, 0xb7, 0x97,
|
|
0xef, 0xfb, 0x90, 0x9c, 0x9d, 0x41, 0x3e, 0x0c, 0x82, 0x18, 0x7a, 0x96, 0xe6, 0x40, 0x16, 0xe4, 0xff, 0x6b, 0x20, 0xa2, 0x95, 0x7f, 0x59, 0x97, 0x11, 0xf0, 0x4a, 0xf8, 0x2a, 0x1d, 0x72, 0x66,
|
|
0x19, 0xb5, 0x30, 0xc7, 0x48, 0x9a, 0x8e, 0x4f, 0x58, 0x93, 0xd5, 0x98, 0x70, 0x2f, 0x5d, 0x26, 0xa4, 0x14, 0x30, 0x6d, 0x2c, 0x0d, 0xa5, 0x77, 0x7f, 0xdf, 0xfb, 0x32, 0x3a, 0x7b, 0xb8, 0xbc,
|
|
0xec, 0xdf, 0xa7, 0xfc, 0x95, 0xb4, 0x1c, 0x6d, 0x5c, 0x97, 0x89, 0x40, 0xac, 0x18, 0xea, 0xb8, 0xd0, 0x02, 0x8d, 0x3a, 0x23, 0x62, 0xb0, 0xeb, 0xe2, 0xcb, 0x6d, 0xef, 0xe6, 0xea, 0x7c, 0x74,
|
|
0x71, 0xdf, 0xfb, 0x14, 0x81, 0x3f, 0xa5, 0x8c, 0xe2, 0xce, 0xb5, 0x1c, 0x74, 0xfb, 0xe0, 0x1f, 0x3b, 0x62, 0xc4, 0xe5, 0xf5, 0xc7, 0xde, 0xb0, 0x2e, 0x32, 0x42, 0xf2, 0x9b, 0xcd, 0xda, 0x0b,
|
|
0x34, 0x9c, 0x6f, 0xa0, 0xc1, 0xe1, 0x0b, 0xf6, 0x00, 0xa8, 0x0f, 0x85, 0xd5, 0x45, 0x82, 0x91, 0x60, 0xac, 0xf6, 0x73, 0xd4, 0x81, 0x53, 0xde, 0x13, 0x23, 0x1e, 0x6e, 0x07, 0x57, 0xef, 0x6f,
|
|
0xfb, 0x17, 0xa3, 0xb3, 0x2f, 0xc3, 0x3e, 0x2c, 0x49, 0x2f, 0xcc, 0x11, 0x89, 0x0e, 0x96, 0xe6, 0xf4, 0xc9, 0x4a, 0x87, 0xf7, 0x57, 0xbd, 0xdb, 0xf7, 0xd7, 0xfd, 0x01, 0x41, 0xa5, 0xb4, 0x02,
|
|
0xe0, 0x89, 0xaa, 0x85, 0xc4, 0xc8, 0xc9, 0x83, 0xad, 0x41, 0xe6, 0x12, 0xa0, 0x71, 0x44, 0x9a, 0xd7, 0x30, 0x95, 0x01, 0x3b, 0x94, 0xe9, 0x11, 0xf6, 0xa7, 0x12, 0xaa, 0xd4, 0x00, 0xe1, 0x68,
|
|
0x44, 0x76, 0x04, 0x51, 0x18, 0x72, 0xdc, 0x68, 0x0c, 0x04, 0xe5, 0x6c, 0x0e, 0x36, 0x14, 0xfd, 0xd2, 0x6e, 0x69, 0xc8, 0x58, 0xa2, 0xc9, 0xe6, 0x61, 0x99, 0xd9, 0x5e, 0x93, 0x8a, 0x11, 0x1a,
|
|
0xc8, 0x17, 0xcb, 0x14, 0xaa, 0x34, 0x98, 0xc3, 0x71, 0x21, 0x95, 0x0c, 0x3d, 0xa8, 0x03, 0x02, 0x1b, 0x16, 0x4e, 0x38, 0x9f, 0xbb, 0x5e, 0x20, 0xad, 0x1f, 0xfc, 0xcc, 0x24, 0x58, 0x20, 0x94,
|
|
0x28, 0x13, 0xb8, 0xa7, 0x94, 0x37, 0xc4, 0x38, 0x84, 0x76, 0xe1, 0xcb, 0x13, 0x91, 0x4e, 0x72, 0x98, 0x89, 0x72, 0x11, 0x0b, 0x00, 0x0b, 0xdf, 0xe0, 0x71, 0x13, 0x71, 0xad, 0x47, 0xb7, 0xbf,
|
|
0x25, 0xb8, 0xb5, 0x59, 0xf0, 0xb7, 0x05, 0xb7, 0x40, 0x04, 0x37, 0x00, 0x90, 0xf1, 0xed, 0x55, 0xe1, 0x4d, 0x50, 0x10, 0xf1, 0xe2, 0x6b, 0xb3, 0x39, 0x64, 0x48, 0xed, 0x0b, 0x94, 0xcf, 0x96,
|
|
0xa8, 0x27, 0x25, 0xd8, 0xbe, 0xa8, 0xc2, 0x55, 0x3d, 0xd0, 0x6b, 0xca, 0xe2, 0xfb, 0x6a, 0xd9, 0x95, 0xd1, 0x2f, 0x67, 0x00, 0x9b, 0x03, 0x20, 0x02, 0x64, 0xc2, 0x5f, 0xc6, 0x63, 0x61, 0xef,
|
|
0xd5, 0x4c, 0x9b, 0xf2, 0x2b, 0x5c, 0x49, 0x60, 0xcb, 0xd5, 0x10, 0x97, 0xe5, 0x8c, 0xcf, 0x0c, 0x58, 0xab, 0x75, 0xb6, 0x84, 0x42, 0x06, 0x96, 0x56, 0x9d, 0x99, 0x89, 0xa7, 0x5a, 0x08, 0x0f,
|
|
0xf5, 0xe3, 0x07, 0x34, 0x66, 0x7d, 0x55, 0xb9, 0x28, 0x86, 0xe2, 0x14, 0x32, 0xaa, 0xdc, 0x60, 0x9a, 0x03, 0x1e, 0x17, 0x6d, 0x25, 0xb0, 0x20, 0x92, 0x2e, 0x38, 0x14, 0x6a, 0xce, 0xdb, 0x00,
|
|
0xfc, 0x2d, 0x8f, 0x18, 0x16, 0xd1, 0x4d, 0x34, 0x5b, 0x0e, 0xe8, 0x09, 0xa2, 0x22, 0x3a, 0x60, 0xa8, 0x65, 0xbd, 0x40, 0x91, 0x24, 0xc8, 0x35, 0x23, 0x2c, 0x9f, 0xd5, 0x4a, 0xf1, 0x22, 0x4a,
|
|
0x22, 0xa9, 0x5c, 0x43, 0x10, 0x4a, 0x99, 0xa4, 0x4b, 0x03, 0xbe, 0x9f, 0xe2, 0x76, 0x5a, 0xff, 0xfd, 0xcf, 0xc3, 0x87, 0xfb, 0x7e, 0x33, 0x8a, 0x45, 0x50, 0x66, 0xb7, 0x0d, 0x89, 0x45, 0xf6,
|
|
0x8d, 0xda, 0x17, 0x29, 0x07, 0x17, 0x0d, 0x06, 0xb0, 0xba, 0xa4, 0x42, 0xf2, 0x76, 0xeb, 0x06, 0x5c, 0x84, 0xaa, 0xd0, 0xb1, 0xad, 0x47, 0x91, 0x5b, 0x38, 0xa0, 0x0c, 0x2a, 0x51, 0x51, 0xa5,
|
|
0xee, 0x9c, 0x7b, 0xa4, 0x96, 0x7a, 0xc2, 0xb7, 0xa9, 0x3d, 0x01, 0x73, 0x10, 0xd2, 0x96, 0xb0, 0x4a, 0x41, 0x34, 0x13, 0x9f, 0x07, 0x51, 0x26, 0x22, 0x74, 0xb9, 0x27, 0xd8, 0x8d, 0x95, 0x8d,
|
|
0x89, 0x55, 0x4b, 0x51, 0xc9, 0x17, 0x5a, 0x7c, 0x81, 0x8b, 0xf5, 0x13, 0xee, 0x2a, 0xd4, 0x59, 0x23, 0xd7, 0xfc, 0x81, 0x76, 0x11, 0x10, 0xbe, 0xa1, 0xec, 0xef, 0x2e, 0x8b, 0x86, 0x28, 0xfb,
|
|
0x8d, 0x55, 0xc1, 0x98, 0x28, 0x4d, 0x1a, 0x9a, 0xdc, 0xa7, 0x7a, 0x5b, 0x64, 0xff, 0x3e, 0x13, 0x9e, 0xe0, 0x8f, 0x10, 0x0a, 0x5f, 0x43, 0x61, 0x0c, 0x78, 0x06, 0x87, 0x01, 0x6d, 0x0b, 0x73,
|
|
0x45, 0xfc, 0x1a, 0x7c, 0xa2, 0x85, 0x76, 0xe0, 0x4b, 0xee, 0x30, 0x7c, 0x4b, 0x6e, 0x40, 0xb9, 0x71, 0x15, 0x61, 0xc5, 0xe2, 0x8b, 0x85, 0xfc, 0xe9, 0xbe, 0x77, 0x37, 0x1a, 0xca, 0xca, 0xe2,
|
|
0xba, 0x77, 0x03, 0x37, 0x1f, 0x47, 0xfd, 0x8b, 0xf7, 0xfd, 0xc8, 0x71, 0xbf, 0x66, 0xfc, 0xe0, 0xaf, 0x8f, 0x87, 0xdc, 0x64, 0x74, 0x79, 0x75, 0x3d, 0xec, 0xdf, 0x8b, 0xf6, 0xdb, 0x7e, 0xef,
|
|
0xbe, 0x3f, 0x18, 0x46, 0x79, 0x26, 0x0e, 0xa7, 0x65, 0x82, 0xab, 0x11, 0xc7, 0x36, 0x05, 0xd8, 0xfd, 0xfb, 0xb3, 0x9e, 0x58, 0x1e, 0xd9, 0x26, 0x90, 0x79, 0x41, 0x38, 0x4a, 0x2f, 0x2d, 0x21,
|
|
0xe2, 0x97, 0x92, 0x06, 0xc0, 0x81, 0x61, 0xf2, 0x12, 0x5d, 0x4a, 0xa7, 0x2d, 0x03, 0xe5, 0xd7, 0x66, 0x1d, 0xff, 0xc1, 0x94, 0xf4, 0x83, 0x9f, 0x45, 0xdd, 0x8c, 0x2e, 0x19, 0x6b, 0x46, 0xed,
|
|
0xb9, 0x56, 0xb8, 0x61, 0xdf, 0x20, 0xae, 0x4a, 0xa4, 0xe9, 0xd8, 0x2b, 0x51, 0x02, 0x70, 0x7b, 0x7f, 0x3f, 0x7d, 0x19, 0xdd, 0xb2, 0xe6, 0x86, 0x56, 0x79, 0x29, 0xf1, 0x1e, 0xc4, 0x59, 0xf9,
|
|
0xa6, 0x24, 0xfa, 0xb9, 0x8b, 0x61, 0x67, 0x80, 0xe1, 0xcb, 0x84, 0x34, 0xce, 0x5f, 0x68, 0xf3, 0x28, 0xa3, 0xc5, 0xa0, 0xa1, 0x43, 0xa6, 0x05, 0x39, 0xdc, 0x78, 0xc5, 0x4c, 0xf4, 0x13, 0x90,
|
|
0xeb, 0x89, 0x58, 0x47, 0x89, 0xde, 0x48, 0x44, 0x31, 0xee, 0x8d, 0x0c, 0xac, 0xa6, 0xe2, 0x3d, 0x29, 0x40, 0x08, 0x16, 0x29, 0xdc, 0x4a, 0x26, 0x22, 0xe7, 0x1c, 0xdd, 0xb9, 0xcd, 0x35, 0xef,
|
|
0x12, 0x31, 0xc5, 0x9e, 0x4e, 0x2e, 0x4b, 0xf2, 0x6b, 0xe2, 0xf2, 0x07, 0x78, 0xb3, 0xa8, 0x34, 0x46, 0x78, 0x72, 0xc5, 0x55, 0x56, 0xad, 0x52, 0xf7, 0xe9, 0x69, 0x7b, 0xaf, 0xf6, 0x4b, 0x73,
|
|
0x39, 0x99, 0xd4, 0x54, 0x60, 0x5d, 0xa9, 0x27, 0x1d, 0xad, 0x83, 0x0d, 0x1d, 0x87, 0x1b, 0xda, 0x9b, 0xe9, 0x76, 0x8c, 0x2e, 0xa9, 0x59, 0xab, 0xb2, 0xbe, 0xbe, 0xfe, 0x78, 0x2f, 0xb3, 0xbc,
|
|
0xd1, 0xd9, 0xd5, 0x90, 0xfd, 0x48, 0x35, 0x5f, 0xf4, 0xef, 0x86, 0x1f, 0xa8, 0x95, 0xf6, 0x8b, 0xb2, 0xac, 0x42, 0x9d, 0x72, 0x01, 0xce, 0xd5, 0xc1, 0xc8, 0xe4, 0x57, 0xe7, 0x60, 0x9d, 0x73,
|
|
0x93, 0x98, 0xfc, 0xd0, 0xef, 0xdd, 0xb5, 0x0e, 0xbe, 0xce, 0x17, 0x40, 0xf0, 0xb7, 0x64, 0x13, 0x8a, 0x36, 0x25, 0xbb, 0x49, 0xb7, 0x99, 0xed, 0x16, 0x9b, 0x91, 0xd9, 0x69, 0x3e, 0xe2, 0x24,
|
|
0x3d, 0xb9, 0xbd, 0x39, 0x84, 0xc9, 0x28, 0x80, 0xc9, 0x82, 0x18, 0x0d, 0x0c, 0x32, 0x3d, 0x68, 0xc3, 0x90, 0x24, 0x7a, 0xd5, 0x56, 0xb3, 0xd9, 0x54, 0x68, 0x8f, 0x4f, 0xe4, 0x55, 0x90, 0xdf,
|
|
0xfb, 0x2e, 0x18, 0x7d, 0x9c, 0xbd, 0xe4, 0xdb, 0x47, 0x14, 0x20, 0x4e, 0xc0, 0xab, 0xe1, 0x76, 0xf2, 0x7a, 0x37, 0xb8, 0x6b, 0x07, 0xca, 0x64, 0x6e, 0x64, 0x6c, 0xe1, 0x0e, 0x5c, 0xbf, 0x4d,
|
|
0xd9, 0x0f, 0xb8, 0x55, 0x66, 0x6b, 0xde, 0x14, 0x7e, 0x75, 0x3d, 0x9c, 0x85, 0xb6, 0x86, 0xb0, 0xc2, 0xdc, 0x94, 0x72, 0xaa, 0x6d, 0x24, 0x93, 0xce, 0x93, 0x38, 0x2b, 0x8d, 0x57, 0xdb, 0x5e,
|
|
0x93, 0x3e, 0x28, 0xe4, 0xf5, 0x01, 0xa3, 0xb9, 0xad, 0xe9, 0x48, 0x21, 0x10, 0x57, 0x46, 0xa7, 0x08, 0x56, 0x38, 0x32, 0xe1, 0xbe, 0x73, 0xb8, 0xd7, 0x8d, 0x1b, 0x16, 0xd0, 0xb0, 0xdf, 0x6a,
|
|
0x13, 0x71, 0x1f, 0x29, 0x3d, 0x92, 0xdb, 0x96, 0xb8, 0x35, 0xcb, 0xcb, 0xba, 0x33, 0x99, 0x8e, 0x74, 0x19, 0xf1, 0xd1, 0x22, 0xc4, 0x27, 0x1d, 0xa6, 0x07, 0xe0, 0x18, 0x06, 0xb0, 0x54, 0xaa,
|
|
0x71, 0x00, 0x7e, 0xb3, 0x59, 0x16, 0xe9, 0x50, 0x4c, 0x79, 0xa2, 0x0c, 0x66, 0x19, 0xbf, 0xdf, 0x15, 0x7d, 0x66, 0xae, 0xef, 0x83, 0x54, 0xb4, 0x0c, 0xf2, 0x27, 0x10, 0x5d, 0x43, 0x48, 0xfa,
|
|
0x26, 0x50, 0x1e, 0x01, 0xe2, 0x72, 0x09, 0x37, 0x78, 0x22, 0x7c, 0x98, 0x47, 0x43, 0x86, 0x23, 0xf2, 0xfd, 0x08, 0x69, 0xc9, 0x2c, 0xea, 0x8d, 0xd1, 0x3e, 0x97, 0x4b, 0x2f, 0x2e, 0x65, 0xf4,
|
|
0x54, 0x39, 0xce, 0x14, 0x1f, 0x98, 0xaf, 0xae, 0x95, 0x0b, 0x5f, 0x69, 0xcb, 0xf5, 0xf0, 0x5d, 0x9d, 0x1d, 0xec, 0xc3, 0x2f, 0xf9, 0x43, 0x06, 0x56, 0x7d, 0x48, 0x8e, 0x18, 0xec, 0xf7, 0x10,
|
|
0x9d, 0x1d, 0x2e, 0x2b, 0x55, 0xad, 0x0c, 0x3e, 0xf5, 0xee, 0x2a, 0x19, 0xeb, 0xfd, 0x40, 0x59, 0xf5, 0x8d, 0x2b, 0xf6, 0x75, 0x44, 0x8a, 0x03, 0x29, 0x64, 0x92, 0xe1, 0xbc, 0x20, 0xdf, 0x72,
|
|
0xe9, 0x67, 0xc8, 0x7c, 0x07, 0x24, 0xbe, 0xfb, 0x15, 0x48, 0x84, 0xfa, 0xe4, 0xdd, 0x21, 0xa5, 0x53, 0x4b, 0x22, 0x13, 0x66, 0x5c, 0xd1, 0x45, 0x33, 0xfa, 0x8f, 0x04, 0x08, 0xa2, 0x21, 0xf0,
|
|
0x56, 0x6e, 0x3e, 0x0e, 0x6f, 0x8b, 0x88, 0x3e, 0x0b, 0x83, 0x20, 0x45, 0x74, 0x00, 0xb9, 0x84, 0xe1, 0x2e, 0x9c, 0x7f, 0x80, 0xf4, 0x83, 0x03, 0x20, 0x7b, 0x7f, 0x3b, 0xe9, 0x38, 0x3d, 0x51,
|
|
0x92, 0xa2, 0xfe, 0xec, 0xa1, 0x98, 0xfa, 0x7f, 0x73, 0x40, 0xfd, 0xc8, 0x57, 0xff, 0x18, 0xd1, 0xef, 0x80, 0xe0, 0x03, 0x94, 0x37, 0xfc, 0x21, 0x03, 0x82, 0xc4, 0x64, 0xc6, 0x84, 0xc4, 0x7f,
|
|
0xf7, 0xbf, 0x9c, 0x11, 0x89, 0xb8, 0x30, 0xff, 0xb1, 0x0f, 0x46, 0xa7, 0x7b, 0xb9, 0xad, 0x17, 0xc9, 0xc2, 0xc7, 0xdc, 0x7d, 0x64, 0x6b, 0xe8, 0x1c, 0x70, 0xe1, 0x8c, 0xf0, 0x09, 0xc8, 0xa8,
|
|
0x0d, 0xeb, 0x40, 0xd9, 0xef, 0x96, 0xd1, 0x81, 0x8f, 0xce, 0x7a, 0xc3, 0xf3, 0x0f, 0xd0, 0xd0, 0x3e, 0x68, 0xb7, 0xf6, 0xc0, 0x99, 0x94, 0xa9, 0x19, 0x77, 0x6b, 0xb0, 0x82, 0xfe, 0x4d, 0xba,
|
|
0xaa, 0x4c, 0x66, 0x90, 0x8c, 0xdb, 0xe9, 0xd4, 0xba, 0x19, 0xf8, 0xf3, 0x02, 0xd7, 0x96, 0x80, 0xa7, 0x80, 0xef, 0x52, 0x0e, 0x2d, 0x13, 0x51, 0x2e, 0xed, 0xd0, 0x37, 0xef, 0xa9, 0x02, 0x14,
|
|
0x5e, 0xe8, 0xa5, 0xbc, 0x25, 0x43, 0x6a, 0x3d, 0x73, 0x7b, 0x9e, 0xdc, 0x8a, 0xb9, 0x30, 0x3b, 0x2b, 0x98, 0x3d, 0x6d, 0x39, 0x38, 0xcd, 0xff, 0x85, 0x1a, 0xa8, 0x5b, 0x5f, 0x82, 0x2e, 0xf5,
|
|
0x15, 0x7e, 0x2d, 0x5b, 0x78, 0x85, 0x5f, 0xcb, 0x36, 0x5e, 0xe1, 0xd7, 0xb2, 0x83, 0x57, 0x1d, 0xd0, 0xb0, 0xd8, 0x89, 0x3d, 0xe3, 0xba, 0x86, 0x65, 0xc5, 0x1f, 0x30, 0x38, 0xca, 0x5c, 0x21,
|
|
0x36, 0xac, 0xb0, 0xd6, 0x98, 0x84, 0xb6, 0x58, 0x71, 0x56, 0xf0, 0xd6, 0x67, 0x63, 0xcc, 0x1f, 0x02, 0x17, 0xec, 0x84, 0xcf, 0x93, 0x3d, 0x9f, 0x20, 0x62, 0x8a, 0x59, 0x13, 0x36, 0x77, 0x7d,
|
|
0xdf, 0x1a, 0xdb, 0xf8, 0x00, 0x47, 0x6e, 0x29, 0x81, 0x07, 0xf5, 0x01, 0x15, 0x23, 0xdd, 0x6a, 0xde, 0xd8, 0x02, 0xaf, 0xe6, 0xad, 0xc4, 0x6c, 0xb2, 0x4a, 0xc9, 0x72, 0x76, 0x7a, 0xc2, 0x12,
|
|
0xa9, 0x37, 0x20, 0x5b, 0x5a, 0x17, 0x6d, 0x94, 0x12, 0xc6, 0xe2, 0xfb, 0x9a, 0x41, 0xb1, 0xd3, 0xd9, 0x6d, 0x52, 0x60, 0x5e, 0x42, 0xe1, 0xcb, 0x5e, 0x06, 0x14, 0x11, 0x7c, 0xd5, 0xec, 0x6e,
|
|
0xc3, 0xd8, 0x11, 0x18, 0x5b, 0x5b, 0x31, 0xee, 0x09, 0x8c, 0xad, 0xad, 0x18, 0x0f, 0x04, 0xc6, 0xf6, 0x56, 0x8c, 0xef, 0x04, 0xc6, 0xf6, 0x56, 0x8c, 0xbf, 0xbe, 0x16, 0x63, 0xab, 0xf9, 0x5a,
|
|
0x94, 0xad, 0x76, 0xcc, 0xf7, 0x16, 0xc0, 0xce, 0x6b, 0xf9, 0x6e, 0xed, 0x0b, 0x94, 0x9d, 0xad, 0x28, 0x85, 0x84, 0x56, 0x9d, 0x1c, 0xca, 0xf3, 0x2c, 0xa4, 0x54, 0x76, 0x9c, 0x21, 0x6c, 0x81,
|
|
0x6e, 0xfd, 0x14, 0x74, 0xfb, 0xa7, 0xa0, 0x3b, 0x3f, 0x05, 0xbd, 0xf7, 0x53, 0xd0, 0xfb, 0x1b, 0xa1, 0xc5, 0xba, 0xd9, 0x3d, 0x61, 0x07, 0xeb, 0x49, 0xee, 0x10, 0x3c, 0xcf, 0x9d, 0xb5, 0xe4,
|
|
0x90, 0xc6, 0xa3, 0x33, 0x58, 0xb5, 0x44, 0x34, 0x59, 0xb6, 0x76, 0x77, 0xbb, 0x70, 0x07, 0xdf, 0x22, 0xc9, 0xb1, 0xdb, 0x72, 0x77, 0x69, 0xcd, 0xe1, 0xc6, 0x00, 0xe1, 0x26, 0x88, 0xdd, 0x26,
|
|
0x7a, 0xe4, 0x52, 0xe2, 0x81, 0x96, 0xad, 0x06, 0x80, 0xe3, 0x6c, 0xe2, 0x17, 0x66, 0xb3, 0xdb, 0xe9, 0xdb, 0xa8, 0x9b, 0x9a, 0xe3, 0x5e, 0xf8, 0x91, 0x79, 0xfa, 0x3a, 0x0f, 0x03, 0xb1, 0xed,
|
|
0x1c, 0x71, 0x01, 0xa3, 0x70, 0x48, 0x9b, 0xb8, 0x51, 0xd5, 0xec, 0x06, 0xf9, 0xb2, 0xc5, 0x76, 0x59, 0x05, 0xfe, 0xed, 0x22, 0xbf, 0xd1, 0xe5, 0xb2, 0x9d, 0xb4, 0xb6, 0x45, 0x29, 0x81, 0x7c,
|
|
0x59, 0x00, 0x7d, 0x02, 0x43, 0x24, 0x9b, 0xd6, 0x0a, 0x6f, 0x57, 0xf1, 0xed, 0x12, 0xe5, 0xb2, 0x6c, 0xc7, 0xbd, 0x78, 0x8b, 0xcb, 0x46, 0xdc, 0x1b, 0xb8, 0x95, 0x02, 0x30, 0x0d, 0x2b, 0x46,
|
|
0x60, 0xac, 0xb0, 0x69, 0x05, 0x4d, 0x09, 0x92, 0x19, 0x3d, 0x95, 0x6c, 0x29, 0xea, 0x8d, 0x16, 0x98, 0x8a, 0xff, 0x87, 0x17, 0x54, 0x8d, 0xe5, 0x8e, 0xb1, 0xdc, 0x35, 0x56, 0x3b, 0xc6, 0x0a,
|
|
0x9d, 0x3b, 0x20, 0xda, 0x39, 0x21, 0x40, 0xbc, 0x59, 0xa5, 0x6e, 0x10, 0x01, 0x24, 0x9d, 0xa6, 0x8b, 0x53, 0x01, 0xfc, 0x4b, 0x3a, 0x22, 0x38, 0x98, 0xbf, 0x01, 0xd8, 0x37, 0xc0, 0x81, 0xc7,
|
|
0x45, 0x41, 0x00, 0xaa, 0xa5, 0x8a, 0x02, 0x11, 0xaa, 0x43, 0xbe, 0xb0, 0x6d, 0x95, 0x6e, 0x03, 0xc1, 0x34, 0x24, 0x5c, 0x23, 0x86, 0x13, 0x6d, 0xab, 0xa4, 0x2d, 0x71, 0xf4, 0xee, 0xd4, 0xd2,
|
|
0xb1, 0x3e, 0xb5, 0x1c, 0xdd, 0xf5, 0x3c, 0xc8, 0x4b, 0x14, 0xf6, 0xf9, 0xf3, 0x67, 0x76, 0x79, 0xf5, 0xf9, 0xa6, 0xaf, 0xa4, 0xcd, 0xa3, 0x4a, 0xa8, 0x25, 0x57, 0x35, 0xa8, 0xf6, 0x08, 0xad,
|
|
0xa4, 0x9e, 0xee, 0x49, 0x83, 0xd9, 0xfe, 0xdd, 0x4c, 0x7f, 0x3b, 0x37, 0xbe, 0x9d, 0x19, 0x4f, 0x6a, 0x4b, 0x23, 0x20, 0xc5, 0xc5, 0x18, 0x8a, 0x8a, 0x41, 0xb4, 0xb1, 0x7b, 0xa0, 0x99, 0x42,
|
|
0x59, 0xa1, 0x95, 0xfd, 0x37, 0xd6, 0x92, 0x62, 0x1e, 0x30, 0xd4, 0x61, 0x58, 0x1d, 0x60, 0xe5, 0x2f, 0xde, 0xb7, 0xe5, 0x7d, 0x7b, 0x13, 0x6d, 0x77, 0xae, 0x0d, 0x99, 0xdb, 0x1c, 0x77, 0xf2,
|
|
0x7d, 0xf9, 0x6c, 0x42, 0x50, 0x25, 0x2e, 0x65, 0x45, 0x4e, 0xb3, 0x62, 0x6e, 0xf0, 0x23, 0xba, 0x83, 0xe8, 0xec, 0x47, 0x5b, 0xf3, 0x8d, 0x76, 0x77, 0x7b, 0xc0, 0xc5, 0x01, 0x3b, 0x9d, 0x4d,
|
|
0x51, 0x77, 0x82, 0x75, 0x3e, 0xcd, 0xd0, 0x85, 0x9f, 0x63, 0x42, 0x0f, 0x57, 0xbb, 0xbb, 0x51, 0xe1, 0xf3, 0x8a, 0xb0, 0x2c, 0x98, 0xf8, 0xda, 0xfc, 0xd6, 0xdd, 0x0a, 0xdf, 0x4a, 0xc1, 0xb7,
|
|
0x5e, 0x01, 0xdf, 0x49, 0xc1, 0x5b, 0x3b, 0x6d, 0xf0, 0xde, 0xdb, 0xc7, 0xec, 0xe5, 0xc6, 0x74, 0x5e, 0x31, 0xe6, 0x20, 0x37, 0x66, 0xef, 0x15, 0x63, 0xde, 0xe5, 0xc6, 0xec, 0x7f, 0x43, 0x81,
|
|
0xfe, 0x64, 0x60, 0xfb, 0xc9, 0xc8, 0xf6, 0x73, 0xa1, 0x2d, 0x05, 0x1f, 0x87, 0x94, 0x0e, 0x15, 0xa4, 0x79, 0x8b, 0x1c, 0xf0, 0xe0, 0x1a, 0xca, 0x5d, 0x2a, 0x68, 0xab, 0xe2, 0x20, 0x98, 0xb0,
|
|
0xc6, 0x4d, 0x29, 0x3b, 0xdd, 0xaa, 0x6d, 0xa5, 0xd9, 0x85, 0x4c, 0x5f, 0x7a, 0x18, 0x2a, 0x2a, 0x52, 0xa7, 0xa1, 0x2c, 0xfd, 0xf1, 0x53, 0x54, 0xb9, 0x24, 0x45, 0x7c, 0xd1, 0x16, 0x4a, 0x83,
|
|
0x15, 0x6e, 0x82, 0x9c, 0xb2, 0x4e, 0x64, 0x86, 0x9b, 0x8e, 0x35, 0xb1, 0x8a, 0x38, 0xea, 0x55, 0x61, 0x35, 0xc5, 0x82, 0xe2, 0xc9, 0xfb, 0x30, 0xbc, 0xb9, 0x06, 0xfa, 0x2a, 0xe7, 0xa2, 0x94,
|
|
0x82, 0x6a, 0xbc, 0x82, 0x72, 0x03, 0x2a, 0x6f, 0xe5, 0xa1, 0x24, 0x59, 0x64, 0x81, 0x07, 0x2b, 0x6d, 0xd8, 0x7a, 0x29, 0x20, 0xb1, 0x5b, 0x00, 0x5d, 0xb0, 0x13, 0xb3, 0x06, 0x24, 0x4b, 0x10,
|
|
0x90, 0xc3, 0x40, 0xc8, 0x81, 0xb1, 0xca, 0xc2, 0x3f, 0x82, 0x42, 0x0c, 0xa4, 0x16, 0x3d, 0xad, 0x52, 0x68, 0xdf, 0x0f, 0x82, 0x99, 0x6a, 0xa8, 0x0b, 0x5f, 0xd5, 0x67, 0xc6, 0x38, 0x9c, 0x88,
|
|
0xb3, 0x16, 0x79, 0x7c, 0xb8, 0x19, 0x0f, 0xe9, 0xf6, 0x70, 0x35, 0x47, 0x42, 0xdf, 0x6a, 0x58, 0xd4, 0x88, 0x6d, 0x99, 0xb7, 0x45, 0xd0, 0xae, 0xe3, 0xce, 0xc5, 0x49, 0x36, 0xa9, 0x15, 0xd0,
|
|
0x43, 0x89, 0x04, 0xfa, 0x97, 0x25, 0xca, 0x0d, 0x12, 0xe8, 0x8b, 0xd2, 0xc0, 0xe7, 0x8f, 0x04, 0x94, 0xec, 0xe1, 0xe0, 0xed, 0x73, 0x21, 0x85, 0x33, 0xee, 0xfb, 0xda, 0x94, 0xa7, 0x88, 0x64,
|
|
0x55, 0xfe, 0x04, 0xe4, 0xc8, 0xa3, 0x35, 0xe8, 0xf5, 0xe8, 0x19, 0x30, 0x35, 0x2a, 0xb8, 0x89, 0x5c, 0x48, 0xc0, 0xcb, 0xca, 0x23, 0xdb, 0xd3, 0xc6, 0xca, 0x78, 0x15, 0xf0, 0x6b, 0xee, 0x4c,
|
|
0xc1, 0xc0, 0x4f, 0xe8, 0x19, 0x0e, 0xf6, 0xd1, 0x3c, 0x25, 0xdc, 0x45, 0x73, 0x75, 0xa0, 0x26, 0xde, 0x2d, 0x2b, 0xc9, 0xe9, 0x13, 0x17, 0x2c, 0x1b, 0x70, 0x49, 0xd0, 0x6e, 0xb6, 0x70, 0xd3,
|
|
0xf0, 0x59, 0x98, 0x16, 0xc6, 0x19, 0xf4, 0xa1, 0x1b, 0xb7, 0xcc, 0x68, 0x2e, 0x39, 0x19, 0xa5, 0x16, 0x02, 0xc3, 0x3a, 0xfc, 0x57, 0x70, 0xc0, 0xdf, 0x04, 0x5e, 0x24, 0xbb, 0x2a, 0x20, 0x4f,
|
|
0x4f, 0x4f, 0x59, 0x7b, 0x0f, 0x3c, 0xf9, 0x2f, 0xb8, 0x63, 0xc6, 0x7e, 0xf9, 0x85, 0x65, 0xbb, 0x0e, 0x6b, 0xec, 0x0d, 0xd4, 0xce, 0x58, 0xea, 0xa7, 0x26, 0xca, 0xa6, 0x52, 0x95, 0x4b, 0x7c,
|
|
0x4e, 0x81, 0x8f, 0x95, 0xd0, 0x00, 0x2d, 0xca, 0xa0, 0xee, 0xb9, 0x86, 0xa7, 0x1c, 0xa8, 0x85, 0xb0, 0x61, 0x23, 0x72, 0x48, 0x2d, 0x31, 0xab, 0xd8, 0x0a, 0xcb, 0x08, 0xdb, 0x8a, 0x28, 0x6e,
|
|
0x80, 0xb7, 0x8a, 0xf2, 0xb1, 0xc2, 0xfe, 0xf6, 0xcb, 0xfd, 0xe0, 0x04, 0x3a, 0x5b, 0x30, 0x80, 0x67, 0x17, 0xba, 0x2c, 0x95, 0xc6, 0x50, 0xd3, 0x3e, 0x8a, 0xeb, 0x67, 0x21, 0xcf, 0x44, 0x23,
|
|
0xb1, 0x85, 0x94, 0xfc, 0x85, 0x15, 0xe8, 0x66, 0x46, 0x46, 0x39, 0xd9, 0x68, 0x50, 0x28, 0x1f, 0x1c, 0xa5, 0x6e, 0x9a, 0x47, 0x0c, 0x9d, 0x05, 0x9e, 0x4e, 0xb4, 0x9c, 0x50, 0x3c, 0x4c, 0x6e,
|
|
0xe0, 0x73, 0x2b, 0xdf, 0x74, 0x43, 0xdb, 0x80, 0xd5, 0xfc, 0x84, 0x7b, 0x99, 0xb8, 0x8d, 0xc8, 0x3d, 0x2e, 0x4c, 0x24, 0x43, 0x8e, 0x40, 0xd3, 0x3a, 0x62, 0xe2, 0x89, 0x36, 0xa3, 0xbd, 0x74,
|
|
0xd1, 0x23, 0x27, 0x25, 0xdd, 0x83, 0x8b, 0xde, 0xa6, 0x7a, 0x40, 0x95, 0xde, 0x25, 0x45, 0x65, 0xe3, 0xa8, 0xac, 0x19, 0x4c, 0xe0, 0xe2, 0x07, 0xf5, 0x25, 0xbd, 0xad, 0x83, 0x74, 0xef, 0xf1,
|
|
0x31, 0x3b, 0x2c, 0x00, 0x3a, 0xcc, 0xc1, 0xd0, 0xa0, 0x3c, 0x50, 0x33, 0x07, 0x84, 0xf3, 0x76, 0x0b, 0x78, 0x7e, 0x4e, 0xb1, 0xde, 0x96, 0xe2, 0x2c, 0xe5, 0xca, 0x15, 0x52, 0x02, 0x22, 0xeb,
|
|
0x4c, 0x26, 0x93, 0x7a, 0xda, 0x72, 0x5b, 0x11, 0x37, 0xd8, 0x53, 0x3c, 0x01, 0x61, 0xee, 0x1c, 0xad, 0xcb, 0xd1, 0xe1, 0xcb, 0x60, 0xc2, 0x17, 0xd2, 0xcf, 0x5e, 0x39, 0x41, 0xeb, 0x40, 0xee,
|
|
0x8a, 0xad, 0xcb, 0x56, 0x1e, 0xb3, 0x50, 0x7c, 0xdb, 0xd2, 0x71, 0xad, 0xee, 0xec, 0xd5, 0xf1, 0x6b, 0xf7, 0x50, 0xec, 0x99, 0x5a, 0xbb, 0x27, 0xed, 0x6e, 0x8e, 0xf6, 0xb8, 0x4c, 0x91, 0xf3,
|
|
0x40, 0x96, 0x53, 0x8f, 0xaf, 0x5b, 0xa9, 0xeb, 0x76, 0xea, 0xba, 0xf3, 0x6d, 0xbb, 0x94, 0xf6, 0xfe, 0xff, 0x79, 0x49, 0xa5, 0xc3, 0x7f, 0x37, 0x37, 0xfb, 0x45, 0xdc, 0x84, 0xb3, 0x79, 0xe0,
|
|
0xcb, 0xf5, 0x28, 0xad, 0x88, 0xb6, 0xf8, 0x13, 0x10, 0x72, 0xeb, 0x7f, 0x9d, 0x57, 0x31, 0xc3, 0xce, 0x1e, 0xb1, 0x2c, 0xd1, 0x5a, 0x98, 0xe4, 0x88, 0x8e, 0x3c, 0xff, 0x22, 0xe5, 0xa6, 0x63,
|
|
0x38, 0x82, 0xb4, 0xad, 0x6c, 0xbd, 0xcb, 0xb1, 0xa5, 0xaa, 0x69, 0x48, 0xbc, 0xb5, 0xad, 0xe0, 0x4d, 0x6a, 0x9a, 0x5c, 0x7e, 0x9d, 0x70, 0xba, 0x5c, 0x6d, 0x5f, 0xed, 0x04, 0x87, 0xf5, 0xc6,
|
|
0x2a, 0x96, 0x56, 0xba, 0x0b, 0x31, 0x54, 0xa1, 0x2f, 0xb7, 0xc2, 0xb3, 0x40, 0x8b, 0xb4, 0xbc, 0x3b, 0xb9, 0x4e, 0x7c, 0x4c, 0x51, 0xc5, 0x5e, 0x40, 0xd1, 0xda, 0xab, 0x15, 0xc2, 0xc4, 0x2a,
|
|
0x49, 0x6f, 0x30, 0xbf, 0x5e, 0x25, 0x6c, 0xb1, 0x63, 0x16, 0x28, 0x04, 0x5a, 0xd3, 0xda, 0x58, 0x3f, 0x7d, 0x41, 0x6a, 0xc9, 0x1c, 0xba, 0x78, 0x59, 0x33, 0x87, 0x39, 0xcd, 0xbc, 0x28, 0x7c,
|
|
0x50, 0xf7, 0x18, 0x72, 0xad, 0xc7, 0x9c, 0x31, 0xa6, 0x41, 0x26, 0xbe, 0xc7, 0xff, 0x20, 0x27, 0x1b, 0x7b, 0xa5, 0xc8, 0x41, 0xb6, 0xd2, 0x70, 0xf4, 0x42, 0x88, 0x4d, 0x29, 0x55, 0x35, 0xf1,
|
|
0x5f, 0x89, 0x36, 0x32, 0x9e, 0x7b, 0x3c, 0x8d, 0x7c, 0xf7, 0x2b, 0x34, 0x0f, 0xf3, 0xfb, 0xdb, 0x21, 0xd3, 0x8f, 0xd7, 0x68, 0x44, 0xde, 0x08, 0xd2, 0x0f, 0xe4, 0xaa, 0x02, 0x64, 0x93, 0xbd,
|
|
0x6c, 0x7c, 0x3c, 0x9c, 0xa6, 0x3d, 0x23, 0xc8, 0xdf, 0xd3, 0x79, 0x50, 0x4a, 0x20, 0xff, 0x95, 0xc9, 0xc4, 0x22, 0x2d, 0x58, 0xc5, 0x52, 0x6d, 0x0d, 0x2a, 0x5b, 0xe8, 0x13, 0xa7, 0xad, 0xe9,
|
|
0x89, 0xf1, 0x74, 0xca, 0x05, 0xd7, 0x5d, 0xd2, 0xbc, 0x62, 0xd0, 0x55, 0x55, 0x92, 0x16, 0xe3, 0xcc, 0xbe, 0xd9, 0x81, 0xfa, 0xde, 0xb6, 0xfc, 0x7f, 0x3d, 0x8a, 0x17, 0xfa, 0x20, 0xfd, 0xcc,
|
|
0xbe, 0x8a, 0x47, 0xc7, 0x70, 0x53, 0x9d, 0xf6, 0xd4, 0x0d, 0x91, 0x1f, 0x18, 0x3c, 0x00, 0xff, 0xca, 0xc0, 0xfc, 0xe8, 0x91, 0x3c, 0x8a, 0x54, 0xbc, 0x1c, 0xa1, 0xb1, 0x66, 0xc3, 0x16, 0x19,
|
|
0xa7, 0x4c, 0x75, 0x95, 0xda, 0x36, 0xab, 0x5d, 0xcf, 0x27, 0x20, 0x2f, 0x51, 0x55, 0x1f, 0x53, 0x4f, 0xa8, 0xd3, 0x44, 0xf9, 0x95, 0x42, 0x92, 0xad, 0xdf, 0xd2, 0x0e, 0xb7, 0xd0, 0xc9, 0x3d,
|
|
0xcb, 0x6f, 0xfa, 0x29, 0x7e, 0x36, 0x19, 0xa7, 0xf0, 0xa5, 0x2d, 0x4f, 0x80, 0x23, 0x74, 0x1c, 0x0a, 0xa0, 0x24, 0x97, 0x2e, 0x50, 0xbf, 0xcf, 0x83, 0xea, 0xfa, 0xa3, 0x66, 0xf0, 0x37, 0xb5,
|
|
0xfa, 0x0b, 0x29, 0xf3, 0x16, 0x1a, 0xc0, 0x4c, 0x32, 0x49, 0xbd, 0xba, 0xd7, 0x8d, 0x28, 0x2a, 0xae, 0x36, 0x74, 0xdb, 0xf5, 0xd3, 0xb5, 0x86, 0x2c, 0x35, 0xa2, 0xaa, 0x28, 0x97, 0x2c, 0xbb,
|
|
0x9e, 0x0e, 0xfa, 0xa5, 0x31, 0x8a, 0x2c, 0xc8, 0x5e, 0x57, 0x06, 0x3e, 0xcb, 0x2a, 0x3b, 0xf3, 0x89, 0xeb, 0x1b, 0x5a, 0x70, 0x03, 0x3c, 0xd7, 0x55, 0x95, 0x6f, 0xd7, 0x64, 0xe6, 0xdd, 0x3c,
|
|
0x43, 0x94, 0x1e, 0x17, 0xe9, 0xac, 0xd6, 0xdd, 0xfe, 0x94, 0xbc, 0x5c, 0x2a, 0x25, 0xcf, 0xc9, 0x11, 0xb8, 0xe8, 0x2d, 0xa5, 0xf8, 0x74, 0x99, 0x28, 0xbd, 0x21, 0x87, 0xbd, 0xa0, 0x53, 0x4d,
|
|
0x1a, 0xc8, 0x5d, 0x4f, 0xbf, 0x09, 0x45, 0x8f, 0xb6, 0x85, 0xde, 0x11, 0x30, 0xbb, 0xc0, 0xd6, 0x5f, 0x74, 0x12, 0xaf, 0x75, 0xa8, 0xea, 0xa5, 0xb5, 0x0c, 0xe7, 0xd1, 0x1b, 0x4b, 0xbe, 0xf5,
|
|
0x9d, 0xfb, 0x79, 0x5a, 0xe2, 0xa7, 0xee, 0xf9, 0x27, 0xf8, 0xb9, 0xc3, 0x1b, 0xb4, 0x11, 0xe1, 0x18, 0xee, 0x42, 0x54, 0xaa, 0xf1, 0xf3, 0xf8, 0x82, 0x93, 0x5d, 0xb9, 0x63, 0x1d, 0xb9, 0x91,
|
|
0xf1, 0xb3, 0xfa, 0x92, 0x84, 0xa2, 0x57, 0xed, 0xb0, 0xea, 0x5d, 0x7f, 0xa9, 0x8d, 0x05, 0xee, 0x1c, 0x5f, 0xb8, 0xb3, 0xf9, 0x24, 0x80, 0xdf, 0x4a, 0x4e, 0x0a, 0xaf, 0xa0, 0x39, 0x76, 0xe2,
|
|
0xaf, 0xa6, 0x35, 0xf6, 0xea, 0xdd, 0x52, 0x89, 0xe6, 0x93, 0x12, 0x2b, 0x32, 0x83, 0x48, 0x6b, 0x37, 0xda, 0x23, 0x17, 0xa7, 0xd4, 0x22, 0xf4, 0x74, 0xc2, 0x5b, 0xbe, 0x55, 0x17, 0x9d, 0x60,
|
|
0xa5, 0x03, 0x48, 0x72, 0x7b, 0x97, 0x86, 0x7d, 0xc2, 0x23, 0x7a, 0x78, 0x52, 0xd5, 0xc7, 0xe3, 0x88, 0xe0, 0xd8, 0x80, 0x16, 0x36, 0xd7, 0xa0, 0x2a, 0x86, 0xa0, 0xcd, 0x3d, 0x3a, 0xd5, 0xe7,
|
|
0x7a, 0x50, 0x30, 0xc2, 0x62, 0xc4, 0xbd, 0xe1, 0xc0, 0x4d, 0xbd, 0x4d, 0xa0, 0xe4, 0x18, 0xaa, 0xca, 0x27, 0xd1, 0x05, 0x47, 0xf0, 0x36, 0x9d, 0xbe, 0x03, 0x12, 0x1e, 0xe6, 0x06, 0x9e, 0x63,
|
|
0x89, 0x0f, 0xb5, 0x27, 0x07, 0x79, 0xab, 0x03, 0x5d, 0xb3, 0xb9, 0xea, 0x9b, 0xd6, 0x84, 0xd6, 0xec, 0x86, 0xf3, 0x82, 0x75, 0x3c, 0x44, 0xf6, 0xd3, 0xa7, 0x05, 0x4b, 0x8d, 0xa6, 0xb2, 0x4f,
|
|
0xc7, 0x5e, 0x05, 0x21, 0xe9, 0xcd, 0x8b, 0xfc, 0xa6, 0x59, 0xcf, 0xb1, 0x66, 0x54, 0x1c, 0xd2, 0x52, 0x16, 0xab, 0x38, 0xb7, 0xe1, 0x05, 0xca, 0x4d, 0xaf, 0x73, 0x7a, 0x71, 0x86, 0xb4, 0x98,
|
|
0x1b, 0x9b, 0xc3, 0x25, 0x37, 0x8c, 0x5f, 0x0d, 0x2b, 0x4d, 0x45, 0x33, 0x8c, 0x3e, 0x7a, 0xb2, 0x6b, 0xcb, 0x0f, 0x38, 0x58, 0x74, 0xf5, 0xed, 0xcc, 0x85, 0xe0, 0x34, 0x83, 0xd5, 0xf7, 0xb6,
|
|
0xce, 0xc0, 0x94, 0x4f, 0xd9, 0x9f, 0xb9, 0x03, 0x20, 0x5c, 0x11, 0xe7, 0x2e, 0x3f, 0xd7, 0xe3, 0xcb, 0x2f, 0x78, 0x39, 0xa6, 0xb3, 0x16, 0x98, 0x1e, 0xb3, 0xe7, 0x17, 0x67, 0x08, 0xdc, 0x50,
|
|
0x37, 0x5f, 0x9e, 0x81, 0x40, 0x38, 0x6e, 0x0e, 0x2b, 0xba, 0x6d, 0xc1, 0x68, 0x9a, 0x6c, 0xad, 0x15, 0xe6, 0x6d, 0x6d, 0x9f, 0x8f, 0x38, 0x0a, 0xe7, 0xc9, 0x6c, 0x2c, 0x77, 0x3a, 0xe4, 0x65,
|
|
0x8e, 0xea, 0x58, 0xce, 0x76, 0xa3, 0xd3, 0x5a, 0xc2, 0x55, 0xbf, 0x66, 0x46, 0x3c, 0x41, 0x91, 0xe7, 0xf0, 0xb5, 0x33, 0xb6, 0x36, 0xcc, 0x18, 0xa7, 0x31, 0xeb, 0x73, 0x3e, 0xf2, 0x55, 0xd1,
|
|
0x8c, 0x74, 0x86, 0x84, 0x2b, 0xd0, 0x7b, 0x0e, 0xd9, 0x4d, 0x5a, 0x5e, 0x2f, 0xe3, 0x2a, 0x92, 0x57, 0x1e, 0x57, 0x33, 0xc6, 0x85, 0xc7, 0x42, 0x76, 0x52, 0x67, 0xb0, 0xe8, 0x75, 0x12, 0x61,
|
|
0xc1, 0x22, 0x9f, 0x4a, 0x1f, 0x8a, 0x5e, 0xcb, 0xc5, 0x02, 0xf7, 0x61, 0x78, 0x79, 0x98, 0xce, 0xbc, 0x5e, 0xe3, 0xc1, 0x73, 0x60, 0x1b, 0x9d, 0xa0, 0x89, 0x0b, 0x63, 0x47, 0x15, 0x6f, 0x48,
|
|
0xcb, 0x37, 0x79, 0xe1, 0x12, 0x5f, 0x8d, 0x3e, 0x2d, 0x1f, 0xab, 0xe2, 0x1d, 0xef, 0xff, 0x00, 0x62, 0xbb, 0xd2, 0xb4, 0xfc, 0x3d, 0x00, 0x00, };
|
|
|
|
|
|
//Returns 0 on succses.
|
|
//Returns size of file if non-empty
|
|
//If positive, populates mfi.
|
|
//Returns -1 if can't find file or reached end of file list.
|
|
int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi )
|
|
{
|
|
#ifdef CNFG_DISABLE_HTTP_FILES
|
|
mfi->filelen = 0;
|
|
return -1;
|
|
#else
|
|
if( strcmp( fname, "/" ) == 0 || strcmp( fname, "index.html" ) == 0 )
|
|
{
|
|
mfi->offset = 0;
|
|
mfi->filelen = sizeof(webpage_buffer);
|
|
return MFS_FILE_COMPRESSED_MEMORY;
|
|
}
|
|
else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi )
|
|
{
|
|
#ifdef CNFG_DISABLE_HTTP_FILES
|
|
return 0;
|
|
#else
|
|
//returns # of bytes left tin file.
|
|
if( !mfi->filelen )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int toread = mfi->filelen;
|
|
if( toread > MFS_SECTOR ) toread = MFS_SECTOR;
|
|
memcpy( data, &webpage_buffer[mfi->offset], toread );
|
|
mfi->offset += toread;
|
|
mfi->filelen -= toread;
|
|
return mfi->filelen;
|
|
#endif
|
|
}
|
|
|
|
void MFSClose( struct MFSFileInfo * mfi )
|
|
{
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
//Returns 0 on succses.
|
|
//Returns size of file if non-empty
|
|
//If positive, populates mfi.
|
|
//Returns -1 if can't find file or reached end of file list.
|
|
int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi )
|
|
{
|
|
char targfile[1024];
|
|
|
|
if( strlen( fname ) == 0 || fname[strlen(fname)-1] == '/' )
|
|
{
|
|
snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s/index.html", fname );
|
|
}
|
|
else
|
|
{
|
|
snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s", fname );
|
|
}
|
|
|
|
//printf( ":%s:\n", targfile );
|
|
|
|
FILE * f = mfi->file = fopen( targfile, "rb" );
|
|
if( f <= 0 ) return -1;
|
|
//printf( "F: %p\n", f );
|
|
fseek( f, 0, SEEK_END );
|
|
mfi->filelen = ftell( f );
|
|
fseek( f, 0, SEEK_SET );
|
|
return 0;
|
|
}
|
|
|
|
int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi )
|
|
{
|
|
if( !mfi->filelen )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int toread = fread( data, 1, MFS_SECTOR, mfi->file );
|
|
mfi->filelen -= toread;
|
|
return mfi->filelen;
|
|
}
|
|
|
|
void MFSClose( struct MFSFileInfo * mfi )
|
|
{
|
|
if( mfi->file ) fclose( mfi->file );
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
SHA-1 in C
|
|
By Steve Reid <sreid@sea-to-sky.net>
|
|
100% Public Domain
|
|
|
|
-----------------
|
|
Modified 7/98
|
|
By James H. Brown <jbrown@burgoyne.com>
|
|
Still 100% Public Domain
|
|
|
|
Corrected a problem which generated improper hash values on 16 bit machines
|
|
Routine SHA1Update changed from
|
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
|
len)
|
|
to
|
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
|
long len)
|
|
|
|
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
|
However, on 16 bit machines an int is too small for the shifts being done
|
|
against
|
|
it. This caused the hash function to generate incorrect values if len was
|
|
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
|
|
|
Since the file IO in main() reads 16K at a time, any file 8K or larger would
|
|
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
|
|
"a"s).
|
|
|
|
I also changed the declaration of variables i & j in SHA1Update to
|
|
unsigned long from unsigned int for the same reason.
|
|
|
|
These changes should make no difference to any 32 bit implementations since
|
|
an
|
|
int and a long are the same size in those environments.
|
|
|
|
--
|
|
I also corrected a few compiler warnings generated by Borland C.
|
|
1. Added #include <process.h> for exit() prototype
|
|
2. Removed unused variable 'j' in SHA1Final
|
|
3. Changed exit(0) to return(0) at end of main.
|
|
|
|
ALL changes I made can be located by searching for comments containing 'JHB'
|
|
-----------------
|
|
Modified 8/98
|
|
By Steve Reid <sreid@sea-to-sky.net>
|
|
Still 100% public domain
|
|
|
|
1- Removed #include <process.h> and used return() instead of exit()
|
|
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
|
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
|
|
|
-----------------
|
|
Modified 4/01
|
|
By Saul Kravitz <Saul.Kravitz@celera.com>
|
|
Still 100% PD
|
|
Modified to run on Compaq Alpha hardware.
|
|
|
|
-----------------
|
|
Modified 07/2002
|
|
By Ralph Giles <giles@ghostscript.com>
|
|
Still 100% public domain
|
|
modified for use with stdint types, autoconf
|
|
code cleanup, removed attribution comments
|
|
switched SHA1Final() argument order for consistency
|
|
use SHA1_ prefix for public api
|
|
move public api to sha1.h
|
|
*/
|
|
|
|
/*
|
|
Test Vectors (from FIPS PUB 180-1)
|
|
"abc"
|
|
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
|
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
|
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
|
A million repetitions of "a"
|
|
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
|
*/
|
|
|
|
/* #define SHA1HANDSOFF */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
|
|
|
#define RDrol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
|
|
|
/* blk0() and blk() perform the initial expand. */
|
|
/* I got the idea of expanding during the round function from SSLeay */
|
|
/* FIXME: can we do this in an endian-proof way? */
|
|
#ifdef WORDS_BIGENDIAN
|
|
#define RDblk0(i) block->l[i]
|
|
#else
|
|
#define RDblk0(i) (block->l[i] = (RDrol(block->l[i],24)&0xFF00FF00) \
|
|
|(RDrol(block->l[i],8)&0x00FF00FF))
|
|
#endif
|
|
#define RDblk(i) (block->l[i&15] = RDrol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
|
^block->l[(i+2)&15]^block->l[i&15],1))
|
|
|
|
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
|
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk0(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30);
|
|
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30);
|
|
#define R2(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0x6ED9EBA1+RDrol(v,5);w=RDrol(w,30);
|
|
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+RDblk(i)+0x8F1BBCDC+RDrol(v,5);w=RDrol(w,30);
|
|
#define R4(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0xCA62C1D6+RDrol(v,5);w=RDrol(w,30);
|
|
|
|
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
|
static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
|
{
|
|
uint32_t a, b, c, d, e;
|
|
typedef union {
|
|
uint8_t c[64];
|
|
uint32_t l[16];
|
|
} CHAR64LONG16;
|
|
CHAR64LONG16* block;
|
|
|
|
#ifdef SHA1HANDSOFF
|
|
static uint8_t workspace[64];
|
|
block = (CHAR64LONG16*)workspace;
|
|
memcpy(block, buffer, 64);
|
|
#else
|
|
block = (CHAR64LONG16*)buffer;
|
|
#endif
|
|
|
|
/* Copy context->state[] to working vars */
|
|
a = state[0];
|
|
b = state[1];
|
|
c = state[2];
|
|
d = state[3];
|
|
e = state[4];
|
|
|
|
/* 4 rounds of 20 operations each. Loop unrolled. */
|
|
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
|
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
|
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
|
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
|
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
|
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
|
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
|
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
|
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
|
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
|
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
|
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
|
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
|
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
|
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
|
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
|
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
|
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
|
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
|
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
|
|
|
/* Add the working vars back into context.state[] */
|
|
state[0] += a;
|
|
state[1] += b;
|
|
state[2] += c;
|
|
state[3] += d;
|
|
state[4] += e;
|
|
|
|
/* Wipe variables */
|
|
a = b = c = d = e = 0;
|
|
}
|
|
|
|
|
|
/* SHA1Init - Initialize new context */
|
|
static void RD_SHA1_Init(RD_SHA1_CTX* context)
|
|
{
|
|
/* SHA1 initialization constants */
|
|
context->state[0] = 0x67452301;
|
|
context->state[1] = 0xEFCDAB89;
|
|
context->state[2] = 0x98BADCFE;
|
|
context->state[3] = 0x10325476;
|
|
context->state[4] = 0xC3D2E1F0;
|
|
context->count[0] = context->count[1] = 0;
|
|
}
|
|
|
|
|
|
/* Run your data through this. */
|
|
static void RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len)
|
|
{
|
|
size_t i, j;
|
|
|
|
#ifdef VERBOSE
|
|
SHAPrintContext(context, "before");
|
|
#endif
|
|
|
|
j = (context->count[0] >> 3) & 63;
|
|
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
|
context->count[1] += (len >> 29);
|
|
if ((j + len) > 63) {
|
|
memcpy(&context->buffer[j], data, (i = 64-j));
|
|
RD_SHA1_Transform(context->state, context->buffer);
|
|
for ( ; i + 63 < len; i += 64) {
|
|
RD_SHA1_Transform(context->state, data + i);
|
|
}
|
|
j = 0;
|
|
}
|
|
else i = 0;
|
|
memcpy(&context->buffer[j], &data[i], len - i);
|
|
|
|
#ifdef VERBOSE
|
|
SHAPrintContext(context, "after ");
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Add padding and return the message digest. */
|
|
static void RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context)
|
|
{
|
|
uint32_t i;
|
|
uint8_t finalcount[8];
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
|
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
|
}
|
|
RD_SHA1_Update(context, (uint8_t *)"\200", 1);
|
|
while ((context->count[0] & 504) != 448) {
|
|
RD_SHA1_Update(context, (uint8_t *)"\0", 1);
|
|
}
|
|
RD_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
|
|
for (i = 0; i < RD_SHA1_DIGEST_SIZE; i++) {
|
|
digest[i] = (uint8_t)
|
|
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
|
}
|
|
|
|
/* Wipe variables */
|
|
i = 0;
|
|
memset(context->buffer, 0, 64);
|
|
memset(context->state, 0, 20);
|
|
memset(context->count, 0, 8);
|
|
memset(finalcount, 0, 8); /* SWR */
|
|
|
|
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
|
|
SHA1_Transform(context->state, context->buffer);
|
|
#endif
|
|
}
|
|
|
|
#ifndef CNFGHTTPSERVERONLY
|
|
|
|
/*************************************************************/
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
char * cnfg_http_window_name;
|
|
|
|
int rd_request_fullscreen;
|
|
|
|
uint32_t * transitbuffer;
|
|
int transitlen;
|
|
int transitmax;
|
|
|
|
uint32_t * backbuffer;
|
|
int backbufferlen;
|
|
int backbuffermax;
|
|
|
|
uint32_t * datacmds;
|
|
int curdatacmds;
|
|
int maxdatacmds;
|
|
|
|
short last_dimensions_w;
|
|
short last_dimensions_h;
|
|
|
|
short cnfg_req_w;
|
|
short cnfg_req_h;
|
|
|
|
|
|
void HTTPCustomCallback( )
|
|
{
|
|
if( curhttp->rcb )
|
|
((void(*)())curhttp->rcb)();
|
|
else
|
|
curhttp->isdone = 1;
|
|
}
|
|
|
|
|
|
//Close of curhttp happened.
|
|
void CloseEvent()
|
|
{
|
|
}
|
|
|
|
static void readrdbuffer_websocket_dat( int len )
|
|
{
|
|
do
|
|
{
|
|
int bufferok = TCPCanSend( curhttp->socket, 1340 );
|
|
if( transitlen <= 0 || !bufferok || curhttp->bytesleft == -1 ) return;
|
|
|
|
int offset = transitlen * 4 - curhttp->bytesleft;
|
|
uint8_t * offdata = ((uint8_t*)transitbuffer) + offset;
|
|
int tosend = curhttp->bytesleft;
|
|
if( tosend > 1340 ) tosend = 1340;
|
|
|
|
WebSocketSend( offdata, tosend );
|
|
|
|
//Special - we send an empty frame to indicate competion.
|
|
if( tosend == 0 )
|
|
curhttp->bytesleft = -1;
|
|
else
|
|
curhttp->bytesleft -= tosend;
|
|
} while(1);
|
|
}
|
|
|
|
|
|
|
|
void ConsumeBackBufferForTransit()
|
|
{
|
|
int len = transitlen;
|
|
int maxv = transitmax;
|
|
uint32_t * dcmd = transitbuffer;
|
|
|
|
transitbuffer = backbuffer;
|
|
transitlen = backbufferlen;
|
|
transitmax = backbuffermax;
|
|
|
|
backbuffer = dcmd;
|
|
backbufferlen = 0;
|
|
backbuffermax = maxv;
|
|
}
|
|
|
|
static void readrdbuffer_websocket_cmd( int len )
|
|
{
|
|
uint8_t buf[1300];
|
|
int i;
|
|
|
|
if( len > 1300 ) len = 1300;
|
|
|
|
for( i = 0; i < len; i++ )
|
|
{
|
|
buf[i] = WSPOPMASK();
|
|
}
|
|
|
|
if( strncmp( buf, "SWAP", 4 ) == 0 )
|
|
{
|
|
last_dimensions_w = buf[4] | ( buf[5]<<8 );
|
|
last_dimensions_h = buf[6] | ( buf[7]<<8 );
|
|
ConsumeBackBufferForTransit();
|
|
curhttp->bytesleft = transitlen * 4;
|
|
}
|
|
|
|
if( strncmp( buf, "MOTN", 4 ) == 0 )
|
|
{
|
|
int x = buf[4] | ( buf[5]<<8 );
|
|
int y = buf[6] | ( buf[7]<<8 );
|
|
int but = buf[11];
|
|
HandleMotion( x, y, but );
|
|
}
|
|
|
|
if( strncmp( buf, "BUTN", 4 ) == 0 )
|
|
{
|
|
int x = buf[4] | ( buf[5]<<8 );
|
|
int y = buf[6] | ( buf[7]<<8 );
|
|
int down = buf[10];
|
|
int but = buf[11];
|
|
HandleButton( x, y, but, down );
|
|
}
|
|
|
|
if( strncmp( buf, "KEYB", 4 ) == 0 )
|
|
{
|
|
int key = buf[6];
|
|
int down = buf[7];
|
|
HandleKey( key, down );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NewWebSocket()
|
|
{
|
|
if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws/cmdbuf", 9 ) == 0 )
|
|
{
|
|
printf( "Got connection.\n" );
|
|
curhttp->rcb = (void*)&readrdbuffer_websocket_dat;
|
|
curhttp->rcbDat = (void*)&readrdbuffer_websocket_cmd;
|
|
}
|
|
else
|
|
{
|
|
curhttp->is404 = 1;
|
|
}
|
|
}
|
|
|
|
void WebSocketTick()
|
|
{
|
|
if( curhttp->rcb )
|
|
{
|
|
((void(*)())curhttp->rcb)();
|
|
}
|
|
}
|
|
|
|
void WebSocketData( int len )
|
|
{
|
|
if( curhttp->rcbDat )
|
|
{
|
|
((void(*)( int ))curhttp->rcbDat)( len );
|
|
}
|
|
}
|
|
|
|
void HTTPCustomStart( )
|
|
{
|
|
|
|
curhttp->rcb = 0;
|
|
curhttp->bytesleft = 0;
|
|
|
|
curhttp->isfirst = 1;
|
|
HTTPHandleInternalCallback();
|
|
}
|
|
|
|
void QueueCmds( uint32_t * toqueue, int numwords )
|
|
{
|
|
int origcmds = curdatacmds;
|
|
curdatacmds += numwords;
|
|
if( curdatacmds > maxdatacmds )
|
|
{
|
|
datacmds = realloc( datacmds, curdatacmds*4 );
|
|
maxdatacmds = curdatacmds;
|
|
}
|
|
memcpy( datacmds + origcmds, toqueue, numwords*4 );
|
|
}
|
|
|
|
int CNFGSetup( const char * WindowName, int w, int h )
|
|
{
|
|
RunHTTP( 8888 );
|
|
if( cnfg_http_window_name ) free( cnfg_http_window_name );
|
|
cnfg_http_window_name = strdup( WindowName );
|
|
datacmds = 0;
|
|
backbuffer = 0;
|
|
backbufferlen = 0;
|
|
curdatacmds = 0;
|
|
maxdatacmds = 0;
|
|
rd_request_fullscreen = 0;
|
|
cnfg_req_w = w;
|
|
cnfg_req_h = h;
|
|
return 0;
|
|
}
|
|
|
|
void CNFGSetupFullscreen( const char * WindowName, int screen_number )
|
|
{
|
|
CNFGSetup( WindowName, -1, -1 );
|
|
rd_request_fullscreen = 1;
|
|
}
|
|
|
|
int CNFGHandleInput()
|
|
{
|
|
TickHTTP();
|
|
return 1;
|
|
}
|
|
|
|
// command structure:
|
|
// 0: Continuation
|
|
// 1: Color
|
|
// 2: Pixel
|
|
// 3: SegmentStart
|
|
// 4: RectangleStart
|
|
// 5: PolyStart
|
|
// 6: PolyContinue
|
|
// 7: Blit Pixels
|
|
// 8: Clear frame
|
|
// 9: Swap Buffers (Used as "end of frame")
|
|
// a: CNFGSetLineWidth
|
|
|
|
uint32_t CNFGColor( uint32_t RGBA )
|
|
{
|
|
uint32_t cmds[2] = { 0x10000000, RGBA };
|
|
QueueCmds( cmds, 2 );
|
|
return RGBA;
|
|
}
|
|
|
|
void CNFGTackPixel( short x1, short y1 )
|
|
{
|
|
if( x1 < 0 || x1 > 0x3fff ) return;
|
|
if( y1 < 0 || y1 > 0x3fff ) return;
|
|
uint32_t cmds[1] = { 0x20000000 | (x1 & 0x3fff ) | ( ( y1 & 0x3fff ) << 14 ) };
|
|
QueueCmds( cmds, 1 );
|
|
}
|
|
|
|
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
|
|
{
|
|
uint32_t cmds[3] = {
|
|
0x30000000,
|
|
( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ),
|
|
( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) };
|
|
QueueCmds( cmds, 3 );
|
|
}
|
|
|
|
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
|
|
{
|
|
uint32_t cmds[3] = {
|
|
0x40000000,
|
|
( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ),
|
|
( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) };
|
|
QueueCmds( cmds, 3 );
|
|
}
|
|
|
|
void CNFGSetLineWidth( short width )
|
|
{
|
|
uint32_t cmds[1] = { 0xa0000000 | width };
|
|
QueueCmds( cmds, 1 );
|
|
}
|
|
|
|
void CNFGTackPoly( RDPoint * points, int verts )
|
|
{
|
|
uint32_t * cmds = alloca( 4 * verts + 4 );
|
|
int i;
|
|
cmds[0] = 0x50000000 | verts;
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
uint16_t lx = points[i].x;
|
|
uint16_t ly = points[i].y;
|
|
cmds[i+1] = lx | ( ly << 16 );
|
|
}
|
|
QueueCmds( cmds, verts + 1 );
|
|
}
|
|
|
|
void CNFGClearFrame()
|
|
{
|
|
int sl = strlen( cnfg_http_window_name );
|
|
int blocks = ( sl + 8 + 8 ) / 4;
|
|
uint32_t * cmds = alloca( blocks * 4 );
|
|
cmds[0] = 0x80000000 | blocks | (rd_request_fullscreen<<8) | (sl<<16);
|
|
cmds[1] = CNFGBGColor;
|
|
cmds[2] = cnfg_req_w | ( cnfg_req_h << 16);
|
|
memcpy( cmds+3, cnfg_http_window_name, sl + 1 );
|
|
QueueCmds( cmds, blocks );
|
|
}
|
|
|
|
void CNFGSwapBuffers()
|
|
{
|
|
uint32_t cmds[1] = { 0x90000000 };
|
|
QueueCmds( cmds, 1 );
|
|
|
|
// And swap.
|
|
int len = curdatacmds;
|
|
int maxv = maxdatacmds;
|
|
uint32_t * dcmd = datacmds;
|
|
|
|
datacmds = backbuffer;
|
|
curdatacmds = 0;
|
|
maxdatacmds = backbuffermax;
|
|
|
|
backbuffer = dcmd;
|
|
backbufferlen = len;
|
|
backbuffermax = maxv;
|
|
}
|
|
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
{
|
|
if( w < 0 || h < 0 ) return;
|
|
|
|
uint32_t * cmds = alloca( 4 * w * h + 8 );
|
|
cmds[0] = 0x70000000 | ( w & 0x3fff ) | ( ( h & 0x3fff ) << 14 );
|
|
cmds[1] = ( x | ( y<<16) );
|
|
int i;
|
|
memcpy( cmds + 2, data, w * h * 4 );
|
|
QueueCmds( cmds, w * h + 2 );
|
|
}
|
|
|
|
void CNFGGetDimensions( short * x, short * y )
|
|
{
|
|
*x = last_dimensions_w;
|
|
*y = last_dimensions_h;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#elif defined( __wasm__ )
|
|
//Right now, designed for use with https://github.com/cnlohr/rawdrawwasm/
|
|
#include <CNFG.h>
|
|
#include <stdint.h>
|
|
|
|
extern void __attribute__((import_module("bynsyncify"))) CNFGSwapBuffersInternal();
|
|
void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h );
|
|
void print( double idebug );
|
|
void prints( const char* sdebug );
|
|
|
|
|
|
//Forward declarations that we get from either WASM or our javascript code.
|
|
void CNFGClearFrameInternal( uint32_t bgcolor );
|
|
|
|
//The WASM driver handles internal resizing automatically.
|
|
#ifndef CNFGRASTERIZER
|
|
|
|
void CNFGInternalResize( short x, short y )
|
|
{
|
|
}
|
|
|
|
void CNFGFlushRender()
|
|
{
|
|
if( !CNFGVertPlace ) return;
|
|
CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace );
|
|
CNFGVertPlace = 0;
|
|
}
|
|
void CNFGClearFrame()
|
|
{
|
|
CNFGFlushRender();
|
|
CNFGClearFrameInternal( CNFGBGColor );
|
|
}
|
|
void CNFGSwapBuffers()
|
|
{
|
|
CNFGFlushRender();
|
|
CNFGSwapBuffersInternal( );
|
|
}
|
|
|
|
int CNFGHandleInput()
|
|
{
|
|
//Do nothing.
|
|
//Input is handled on swap frame.
|
|
return 1;
|
|
}
|
|
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
{
|
|
CNFGBlitImageInternal( data, x, y, w, h );
|
|
}
|
|
|
|
#else
|
|
|
|
//Rasterizer - if you want to do this, you will need to enable blitting in the javascript.
|
|
//XXX TODO: NEED MEMORY ALLOCATOR
|
|
extern unsigned char __heap_base;
|
|
unsigned int bump_pointer = (unsigned int)&__heap_base;
|
|
void* malloc(unsigned long size) {
|
|
unsigned int ptr = bump_pointer;
|
|
bump_pointer += size;
|
|
return (void *)ptr;
|
|
}
|
|
void free(void* ptr) { }
|
|
|
|
//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin.
|
|
|
|
#ifdef CNFGRASTERIZER
|
|
//#include <stdlib.h>
|
|
#include <stdint.h>
|
|
|
|
uint32_t * CNFGBuffer = 0;
|
|
short CNFGBufferx;
|
|
short CNFGBuffery;
|
|
|
|
#ifdef CNFGOGL
|
|
void CNFGFlushRender()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
void CNFGInternalResize( short x, short y )
|
|
{
|
|
CNFGBufferx = x;
|
|
CNFGBuffery = y;
|
|
if( CNFGBuffer ) free( CNFGBuffer );
|
|
CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 );
|
|
#ifdef CNFGOGL
|
|
void CNFGInternalResizeOGLBACKEND( short w, short h );
|
|
CNFGInternalResizeOGLBACKEND( x, y );
|
|
#endif
|
|
}
|
|
|
|
#ifdef __wasm__
|
|
static uint32_t SWAPS( uint32_t r )
|
|
{
|
|
uint32_t ret = (r&0xFF)<<24;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<16;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<8;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<0;
|
|
return ret;
|
|
}
|
|
#elif !defined(CNFGOGL)
|
|
#define SWAPS(x) (x>>8)
|
|
#else
|
|
static uint32_t SWAPS( uint32_t r )
|
|
{
|
|
uint32_t ret = (r&0xFF)<<16;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<8;
|
|
r>>=8;
|
|
ret |= (r&0xff);
|
|
r>>=8;
|
|
ret |= (r&0xff)<<24;
|
|
return ret;
|
|
}
|
|
#endif
|
|
uint32_t CNFGColor( uint32_t RGB )
|
|
{
|
|
CNFGLastColor = SWAPS(RGB);
|
|
return CNFGLastColor;
|
|
}
|
|
|
|
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
|
|
{
|
|
short tx, ty;
|
|
//float slope, lp;
|
|
float slope;
|
|
|
|
short dx = x2 - x1;
|
|
short dy = y2 - y1;
|
|
|
|
if( !CNFGBuffer ) return;
|
|
|
|
if( dx < 0 ) dx = -dx;
|
|
if( dy < 0 ) dy = -dy;
|
|
|
|
if( dx > dy )
|
|
{
|
|
short minx = (x1 < x2)?x1:x2;
|
|
short maxx = (x1 < x2)?x2:x1;
|
|
short miny = (x1 < x2)?y1:y2;
|
|
short maxy = (x1 < x2)?y2:y1;
|
|
float thisy = miny;
|
|
slope = (float)(maxy-miny) / (float)(maxx-minx);
|
|
|
|
for( tx = minx; tx <= maxx; tx++ )
|
|
{
|
|
ty = thisy;
|
|
if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue;
|
|
if( tx >= CNFGBufferx ) break;
|
|
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
|
|
thisy += slope;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
short minx = (y1 < y2)?x1:x2;
|
|
short maxx = (y1 < y2)?x2:x1;
|
|
short miny = (y1 < y2)?y1:y2;
|
|
short maxy = (y1 < y2)?y2:y1;
|
|
float thisx = minx;
|
|
slope = (float)(maxx-minx) / (float)(maxy-miny);
|
|
|
|
for( ty = miny; ty <= maxy; ty++ )
|
|
{
|
|
tx = thisx;
|
|
if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue;
|
|
if( ty >= CNFGBuffery ) break;
|
|
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
|
|
thisx += slope;
|
|
}
|
|
}
|
|
}
|
|
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
|
|
{
|
|
short minx = (x1<x2)?x1:x2;
|
|
short miny = (y1<y2)?y1:y2;
|
|
short maxx = (x1>=x2)?x1:x2;
|
|
short maxy = (y1>=y2)?y1:y2;
|
|
|
|
short x, y;
|
|
|
|
if( minx < 0 ) minx = 0;
|
|
if( miny < 0 ) miny = 0;
|
|
if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1;
|
|
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
|
|
|
|
for( y = miny; y <= maxy; y++ )
|
|
{
|
|
uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx];
|
|
for( x = minx; x <= maxx; x++ )
|
|
{
|
|
(*CNFGBufferstart++) = CNFGLastColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CNFGTackPoly( RDPoint * points, int verts )
|
|
{
|
|
short minx = 10000, miny = 10000;
|
|
short maxx =-10000, maxy =-10000;
|
|
short i, x, y;
|
|
|
|
//Just in case...
|
|
if( verts > 32767 ) return;
|
|
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
RDPoint * p = &points[i];
|
|
if( p->x < minx ) minx = p->x;
|
|
if( p->y < miny ) miny = p->y;
|
|
if( p->x > maxx ) maxx = p->x;
|
|
if( p->y > maxy ) maxy = p->y;
|
|
}
|
|
|
|
if( miny < 0 ) miny = 0;
|
|
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
|
|
|
|
for( y = miny; y <= maxy; y++ )
|
|
{
|
|
short startfillx = maxx;
|
|
short endfillx = minx;
|
|
|
|
//Figure out what line segments intersect this line.
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
short pl = i + 1;
|
|
if( pl == verts ) pl = 0;
|
|
|
|
RDPoint ptop;
|
|
RDPoint pbot;
|
|
|
|
ptop.x = points[i].x;
|
|
ptop.y = points[i].y;
|
|
pbot.x = points[pl].x;
|
|
pbot.y = points[pl].y;
|
|
//printf( "Poly: %d %d\n", pbot.y, ptop.y );
|
|
|
|
if( pbot.y < ptop.y )
|
|
{
|
|
RDPoint ptmp;
|
|
ptmp.x = pbot.x;
|
|
ptmp.y = pbot.y;
|
|
pbot.x = ptop.x;
|
|
pbot.y = ptop.y;
|
|
ptop.x = ptmp.x;
|
|
ptop.y = ptmp.y;
|
|
}
|
|
|
|
//Make sure this line segment is within our range.
|
|
//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y );
|
|
if( ptop.y <= y && pbot.y >= y )
|
|
{
|
|
short diffy = pbot.y - ptop.y;
|
|
uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math.
|
|
short diffx = pbot.x - ptop.x;
|
|
short isectx;
|
|
|
|
if( diffy == 0 )
|
|
{
|
|
if( pbot.x < ptop.x )
|
|
{
|
|
if( startfillx > pbot.x ) startfillx = pbot.x;
|
|
if( endfillx < ptop.x ) endfillx = ptop.x;
|
|
}
|
|
else
|
|
{
|
|
if( startfillx > ptop.x ) startfillx = ptop.x;
|
|
if( endfillx < pbot.x ) endfillx = pbot.x;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Inner part is scaled by 65536, outer part must be scaled back.
|
|
isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x;
|
|
if( isectx < startfillx ) startfillx = isectx;
|
|
if( isectx > endfillx ) endfillx = isectx;
|
|
}
|
|
//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx );
|
|
}
|
|
}
|
|
|
|
//printf( "%d %d %d\n", y, startfillx, endfillx );
|
|
|
|
if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1;
|
|
if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1;
|
|
if( startfillx < 0 ) startfillx = 0;
|
|
if( startfillx < 0 ) startfillx = 0;
|
|
|
|
unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx];
|
|
for( x = startfillx; x <= endfillx; x++ )
|
|
{
|
|
(*bufferstart++) = CNFGLastColor;
|
|
}
|
|
}
|
|
//exit(1);
|
|
}
|
|
|
|
|
|
void CNFGClearFrame()
|
|
{
|
|
int i, m;
|
|
uint32_t col = 0;
|
|
short x, y;
|
|
CNFGGetDimensions( &x, &y );
|
|
if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer )
|
|
{
|
|
CNFGBufferx = x;
|
|
CNFGBuffery = y;
|
|
CNFGBuffer = malloc( x * y * 8 );
|
|
}
|
|
|
|
m = x * y;
|
|
col = CNFGColor( CNFGBGColor );
|
|
for( i = 0; i < m; i++ )
|
|
{
|
|
CNFGBuffer[i] = col;
|
|
}
|
|
}
|
|
|
|
void CNFGTackPixel( short x, short y )
|
|
{
|
|
if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
|
|
CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor;
|
|
}
|
|
|
|
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
{
|
|
int ox = x;
|
|
int stride = w;
|
|
if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
|
|
if( x < 0 ) { w += x; x = 0; }
|
|
if( y < 0 ) { h += y; y = 0; }
|
|
|
|
//Switch w,h to x2, y2
|
|
h += y;
|
|
w += x;
|
|
|
|
if( w >= CNFGBufferx ) { w = CNFGBufferx; }
|
|
if( h >= CNFGBuffery ) { h = CNFGBuffery; }
|
|
|
|
|
|
for( ; y < h-1; y++ )
|
|
{
|
|
x = ox;
|
|
uint32_t * indat = data;
|
|
uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x;
|
|
for( ; x < w-1; x++ )
|
|
{
|
|
uint32_t newm = *(indat++);
|
|
uint32_t oldm = *(outdat);
|
|
if( (newm & 0xff) == 0xff )
|
|
{
|
|
*(outdat++) = newm;
|
|
}
|
|
else
|
|
{
|
|
//Alpha blend.
|
|
int alfa = newm&0xff;
|
|
int onemalfa = 255-alfa;
|
|
#ifdef __wasm__
|
|
uint32_t newv = 255<<0; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
uint32_t newv = 255UL<<24; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
#elif defined( ANDROID ) || defined( __android__ )
|
|
uint32_t newv = 255<<16; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#elif defined( CNFGOGL ) //OGL, on X11
|
|
uint32_t newv = 255<<16; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#else //X11
|
|
uint32_t newv = 255UL<<24; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
#endif
|
|
*(outdat++) = newv;
|
|
}
|
|
}
|
|
data += stride;
|
|
}
|
|
}
|
|
|
|
void CNFGSwapBuffers()
|
|
{
|
|
CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery );
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
extern void CNFGUpdateScreenWithBitmapInternal( uint32_t * data, int w, int h );
|
|
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
|
|
{
|
|
CNFGBlitImageInternal( data, 0, 0, w, h );
|
|
CNFGSwapBuffersInternal();
|
|
}
|
|
|
|
|
|
void CNFGSetLineWidth( short width )
|
|
{
|
|
//Rasterizer does not support line width.
|
|
}
|
|
|
|
void CNFGHandleInput()
|
|
{
|
|
//Do nothing.
|
|
//Input is handled on swap frame.
|
|
}
|
|
|
|
#endif
|
|
|
|
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
//Copyright (c) 2011-2019 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
|
|
//Portion from: http://en.wikibooks.org/wiki/Windows_Programming/Window_Creation
|
|
|
|
#ifndef _CNFGWINDRIVER_C
|
|
#define _CNFGWINDRIVER_C
|
|
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <malloc.h> //for alloca
|
|
#include <ctype.h>
|
|
|
|
HBITMAP CNFGlsBitmap;
|
|
HWND CNFGlsHWND;
|
|
HDC CNFGlsWindowHDC;
|
|
HDC CNFGlsHDC;
|
|
HDC CNFGlsHDCBlit;
|
|
|
|
int ShouldClose = 0;
|
|
|
|
//Queue up lines and points for a faster render.
|
|
#ifndef CNFG_WINDOWS_DISABLE_BATCH
|
|
#define BATCH_ELEMENTS
|
|
#endif
|
|
|
|
#define COLORSWAPS( RGB ) \
|
|
((((RGB )& 0xFF000000)>>24) | ( ((RGB )& 0xFF0000 ) >> 8 ) | ( ((RGB )& 0xFF00 )<<8 ))
|
|
|
|
|
|
void CNFGChangeWindowTitle( const char * windowtitle )
|
|
{
|
|
SetWindowTextA( CNFGlsHWND, windowtitle );
|
|
}
|
|
|
|
#ifdef CNFGRASTERIZER
|
|
//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin.
|
|
|
|
#ifdef CNFGRASTERIZER
|
|
//#include <stdlib.h>
|
|
#include <stdint.h>
|
|
|
|
uint32_t * CNFGBuffer = 0;
|
|
short CNFGBufferx;
|
|
short CNFGBuffery;
|
|
|
|
#ifdef CNFGOGL
|
|
void CNFGFlushRender()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
void CNFGInternalResize( short x, short y )
|
|
{
|
|
CNFGBufferx = x;
|
|
CNFGBuffery = y;
|
|
if( CNFGBuffer ) free( CNFGBuffer );
|
|
CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 );
|
|
#ifdef CNFGOGL
|
|
void CNFGInternalResizeOGLBACKEND( short w, short h );
|
|
CNFGInternalResizeOGLBACKEND( x, y );
|
|
#endif
|
|
}
|
|
|
|
#ifdef __wasm__
|
|
static uint32_t SWAPS( uint32_t r )
|
|
{
|
|
uint32_t ret = (r&0xFF)<<24;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<16;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<8;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<0;
|
|
return ret;
|
|
}
|
|
#elif !defined(CNFGOGL)
|
|
#define SWAPS(x) (x>>8)
|
|
#else
|
|
static uint32_t SWAPS( uint32_t r )
|
|
{
|
|
uint32_t ret = (r&0xFF)<<16;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<8;
|
|
r>>=8;
|
|
ret |= (r&0xff);
|
|
r>>=8;
|
|
ret |= (r&0xff)<<24;
|
|
return ret;
|
|
}
|
|
#endif
|
|
uint32_t CNFGColor( uint32_t RGB )
|
|
{
|
|
CNFGLastColor = SWAPS(RGB);
|
|
return CNFGLastColor;
|
|
}
|
|
|
|
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
|
|
{
|
|
short tx, ty;
|
|
//float slope, lp;
|
|
float slope;
|
|
|
|
short dx = x2 - x1;
|
|
short dy = y2 - y1;
|
|
|
|
if( !CNFGBuffer ) return;
|
|
|
|
if( dx < 0 ) dx = -dx;
|
|
if( dy < 0 ) dy = -dy;
|
|
|
|
if( dx > dy )
|
|
{
|
|
short minx = (x1 < x2)?x1:x2;
|
|
short maxx = (x1 < x2)?x2:x1;
|
|
short miny = (x1 < x2)?y1:y2;
|
|
short maxy = (x1 < x2)?y2:y1;
|
|
float thisy = miny;
|
|
slope = (float)(maxy-miny) / (float)(maxx-minx);
|
|
|
|
for( tx = minx; tx <= maxx; tx++ )
|
|
{
|
|
ty = thisy;
|
|
if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue;
|
|
if( tx >= CNFGBufferx ) break;
|
|
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
|
|
thisy += slope;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
short minx = (y1 < y2)?x1:x2;
|
|
short maxx = (y1 < y2)?x2:x1;
|
|
short miny = (y1 < y2)?y1:y2;
|
|
short maxy = (y1 < y2)?y2:y1;
|
|
float thisx = minx;
|
|
slope = (float)(maxx-minx) / (float)(maxy-miny);
|
|
|
|
for( ty = miny; ty <= maxy; ty++ )
|
|
{
|
|
tx = thisx;
|
|
if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue;
|
|
if( ty >= CNFGBuffery ) break;
|
|
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
|
|
thisx += slope;
|
|
}
|
|
}
|
|
}
|
|
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
|
|
{
|
|
short minx = (x1<x2)?x1:x2;
|
|
short miny = (y1<y2)?y1:y2;
|
|
short maxx = (x1>=x2)?x1:x2;
|
|
short maxy = (y1>=y2)?y1:y2;
|
|
|
|
short x, y;
|
|
|
|
if( minx < 0 ) minx = 0;
|
|
if( miny < 0 ) miny = 0;
|
|
if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1;
|
|
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
|
|
|
|
for( y = miny; y <= maxy; y++ )
|
|
{
|
|
uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx];
|
|
for( x = minx; x <= maxx; x++ )
|
|
{
|
|
(*CNFGBufferstart++) = CNFGLastColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CNFGTackPoly( RDPoint * points, int verts )
|
|
{
|
|
short minx = 10000, miny = 10000;
|
|
short maxx =-10000, maxy =-10000;
|
|
short i, x, y;
|
|
|
|
//Just in case...
|
|
if( verts > 32767 ) return;
|
|
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
RDPoint * p = &points[i];
|
|
if( p->x < minx ) minx = p->x;
|
|
if( p->y < miny ) miny = p->y;
|
|
if( p->x > maxx ) maxx = p->x;
|
|
if( p->y > maxy ) maxy = p->y;
|
|
}
|
|
|
|
if( miny < 0 ) miny = 0;
|
|
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
|
|
|
|
for( y = miny; y <= maxy; y++ )
|
|
{
|
|
short startfillx = maxx;
|
|
short endfillx = minx;
|
|
|
|
//Figure out what line segments intersect this line.
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
short pl = i + 1;
|
|
if( pl == verts ) pl = 0;
|
|
|
|
RDPoint ptop;
|
|
RDPoint pbot;
|
|
|
|
ptop.x = points[i].x;
|
|
ptop.y = points[i].y;
|
|
pbot.x = points[pl].x;
|
|
pbot.y = points[pl].y;
|
|
//printf( "Poly: %d %d\n", pbot.y, ptop.y );
|
|
|
|
if( pbot.y < ptop.y )
|
|
{
|
|
RDPoint ptmp;
|
|
ptmp.x = pbot.x;
|
|
ptmp.y = pbot.y;
|
|
pbot.x = ptop.x;
|
|
pbot.y = ptop.y;
|
|
ptop.x = ptmp.x;
|
|
ptop.y = ptmp.y;
|
|
}
|
|
|
|
//Make sure this line segment is within our range.
|
|
//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y );
|
|
if( ptop.y <= y && pbot.y >= y )
|
|
{
|
|
short diffy = pbot.y - ptop.y;
|
|
uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math.
|
|
short diffx = pbot.x - ptop.x;
|
|
short isectx;
|
|
|
|
if( diffy == 0 )
|
|
{
|
|
if( pbot.x < ptop.x )
|
|
{
|
|
if( startfillx > pbot.x ) startfillx = pbot.x;
|
|
if( endfillx < ptop.x ) endfillx = ptop.x;
|
|
}
|
|
else
|
|
{
|
|
if( startfillx > ptop.x ) startfillx = ptop.x;
|
|
if( endfillx < pbot.x ) endfillx = pbot.x;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Inner part is scaled by 65536, outer part must be scaled back.
|
|
isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x;
|
|
if( isectx < startfillx ) startfillx = isectx;
|
|
if( isectx > endfillx ) endfillx = isectx;
|
|
}
|
|
//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx );
|
|
}
|
|
}
|
|
|
|
//printf( "%d %d %d\n", y, startfillx, endfillx );
|
|
|
|
if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1;
|
|
if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1;
|
|
if( startfillx < 0 ) startfillx = 0;
|
|
if( startfillx < 0 ) startfillx = 0;
|
|
|
|
unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx];
|
|
for( x = startfillx; x <= endfillx; x++ )
|
|
{
|
|
(*bufferstart++) = CNFGLastColor;
|
|
}
|
|
}
|
|
//exit(1);
|
|
}
|
|
|
|
|
|
void CNFGClearFrame()
|
|
{
|
|
int i, m;
|
|
uint32_t col = 0;
|
|
short x, y;
|
|
CNFGGetDimensions( &x, &y );
|
|
if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer )
|
|
{
|
|
CNFGBufferx = x;
|
|
CNFGBuffery = y;
|
|
CNFGBuffer = malloc( x * y * 8 );
|
|
}
|
|
|
|
m = x * y;
|
|
col = CNFGColor( CNFGBGColor );
|
|
for( i = 0; i < m; i++ )
|
|
{
|
|
CNFGBuffer[i] = col;
|
|
}
|
|
}
|
|
|
|
void CNFGTackPixel( short x, short y )
|
|
{
|
|
if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
|
|
CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor;
|
|
}
|
|
|
|
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
{
|
|
int ox = x;
|
|
int stride = w;
|
|
if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
|
|
if( x < 0 ) { w += x; x = 0; }
|
|
if( y < 0 ) { h += y; y = 0; }
|
|
|
|
//Switch w,h to x2, y2
|
|
h += y;
|
|
w += x;
|
|
|
|
if( w >= CNFGBufferx ) { w = CNFGBufferx; }
|
|
if( h >= CNFGBuffery ) { h = CNFGBuffery; }
|
|
|
|
|
|
for( ; y < h-1; y++ )
|
|
{
|
|
x = ox;
|
|
uint32_t * indat = data;
|
|
uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x;
|
|
for( ; x < w-1; x++ )
|
|
{
|
|
uint32_t newm = *(indat++);
|
|
uint32_t oldm = *(outdat);
|
|
if( (newm & 0xff) == 0xff )
|
|
{
|
|
*(outdat++) = newm;
|
|
}
|
|
else
|
|
{
|
|
//Alpha blend.
|
|
int alfa = newm&0xff;
|
|
int onemalfa = 255-alfa;
|
|
#ifdef __wasm__
|
|
uint32_t newv = 255<<0; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
uint32_t newv = 255UL<<24; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
#elif defined( ANDROID ) || defined( __android__ )
|
|
uint32_t newv = 255<<16; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#elif defined( CNFGOGL ) //OGL, on X11
|
|
uint32_t newv = 255<<16; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#else //X11
|
|
uint32_t newv = 255UL<<24; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
#endif
|
|
*(outdat++) = newv;
|
|
}
|
|
}
|
|
data += stride;
|
|
}
|
|
}
|
|
|
|
void CNFGSwapBuffers()
|
|
{
|
|
CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery );
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
void InternalHandleResize()
|
|
{
|
|
if( CNFGlsBitmap ) DeleteObject( CNFGlsBitmap );
|
|
|
|
CNFGInternalResize( CNFGBufferx, CNFGBuffery );
|
|
CNFGlsBitmap = CreateBitmap( CNFGBufferx, CNFGBuffery, 1, 32, CNFGBuffer );
|
|
SelectObject( CNFGlsHDC, CNFGlsBitmap );
|
|
CNFGInternalResize( CNFGBufferx, CNFGBuffery);
|
|
}
|
|
#else
|
|
static short CNFGBufferx, CNFGBuffery;
|
|
static void InternalHandleResize();
|
|
#endif
|
|
|
|
|
|
#ifdef CNFGOGL
|
|
#include <GL/gl.h>
|
|
static HGLRC hRC=NULL;
|
|
static void InternalHandleResize() { }
|
|
void CNFGSwapBuffers()
|
|
{
|
|
#ifdef CNFG_BATCH
|
|
#ifndef CNFGCONTEXTONLY
|
|
CNFGFlushRender();
|
|
#endif
|
|
#endif
|
|
|
|
SwapBuffers(CNFGlsWindowHDC);
|
|
}
|
|
#endif
|
|
|
|
void CNFGGetDimensions( short * x, short * y )
|
|
{
|
|
static short lastx, lasty;
|
|
RECT window;
|
|
GetClientRect( CNFGlsHWND, &window );
|
|
CNFGBufferx = (short)( window.right - window.left);
|
|
CNFGBuffery = (short)( window.bottom - window.top);
|
|
if( CNFGBufferx != lastx || CNFGBuffery != lasty )
|
|
{
|
|
lastx = CNFGBufferx;
|
|
lasty = CNFGBuffery;
|
|
#ifndef CNFGCONTEXTONLY
|
|
CNFGInternalResize( lastx, lasty );
|
|
#endif
|
|
InternalHandleResize();
|
|
}
|
|
*x = CNFGBufferx;
|
|
*y = CNFGBuffery;
|
|
}
|
|
|
|
#ifndef CNFGOGL
|
|
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
|
|
{
|
|
RECT r;
|
|
|
|
SelectObject( CNFGlsHDC, CNFGlsBitmap );
|
|
SetBitmapBits(CNFGlsBitmap,w*h*4,data);
|
|
BitBlt(CNFGlsWindowHDC, 0, 0, w, h, CNFGlsHDC, 0, 0, SRCCOPY);
|
|
UpdateWindow( CNFGlsHWND );
|
|
|
|
short thisw, thish;
|
|
|
|
//Check to see if the window is closed.
|
|
if( !IsWindow( CNFGlsHWND ) )
|
|
{
|
|
exit( 0 );
|
|
}
|
|
|
|
GetClientRect( CNFGlsHWND, &r );
|
|
thisw = (short)(r.right - r.left);
|
|
thish = (short)(r.bottom - r.top);
|
|
if( thisw != CNFGBufferx || thish != CNFGBuffery )
|
|
{
|
|
CNFGBufferx = thisw;
|
|
CNFGBuffery = thish;
|
|
InternalHandleResize();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CNFGTearDown()
|
|
{
|
|
PostQuitMessage(0);
|
|
ShouldClose = 1;
|
|
}
|
|
|
|
//This was from the article
|
|
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(msg)
|
|
{
|
|
#ifndef CNFGOGL
|
|
case WM_SYSCOMMAND: //Not sure why, if deactivated, the dc gets unassociated?
|
|
if( wParam == SC_RESTORE || wParam == SC_MAXIMIZE || wParam == SC_SCREENSAVE )
|
|
{
|
|
SelectObject( CNFGlsHDC, CNFGlsBitmap );
|
|
SelectObject( CNFGlsWindowHDC, CNFGlsBitmap );
|
|
}
|
|
break;
|
|
#endif
|
|
case WM_DESTROY:
|
|
HandleDestroy();
|
|
CNFGTearDown();
|
|
return 0;
|
|
}
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen );
|
|
|
|
void CNFGSetupFullscreen( const char * WindowName, int screen_number )
|
|
{
|
|
// Get primary monitor dimensions, but default to 1920x1080
|
|
int monitorW = GetSystemMetrics(SM_CXSCREEN);
|
|
if(0 == monitorW)
|
|
{
|
|
monitorW = 1920;
|
|
}
|
|
|
|
int monitorH = GetSystemMetrics(SM_CYSCREEN);
|
|
if(0 == monitorH)
|
|
{
|
|
monitorH = 1080;
|
|
}
|
|
|
|
CNFGSetupWinInternal(WindowName, monitorW, monitorH, 1);
|
|
}
|
|
|
|
int CNFGSetup( const char * name_of_window, int width, int height )
|
|
{
|
|
return CNFGSetupWinInternal(name_of_window, width, height, 0);
|
|
}
|
|
|
|
//This was from the article, too... well, mostly.
|
|
int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen )
|
|
{
|
|
static LPCSTR szClassName = "MyClass";
|
|
RECT client, window;
|
|
WNDCLASSA wnd;
|
|
int w, h, wd, hd;
|
|
int show_window = 1;
|
|
HINSTANCE hInstance = GetModuleHandle(NULL);
|
|
|
|
if( width < 0 )
|
|
{
|
|
show_window = 0;
|
|
width = -width;
|
|
}
|
|
if( height < 0 )
|
|
{
|
|
show_window = 0;
|
|
height = -height;
|
|
}
|
|
|
|
CNFGBufferx = (short)width;
|
|
CNFGBuffery = (short)height;
|
|
|
|
wnd.style = CS_HREDRAW | CS_VREDRAW; //we will explain this later
|
|
wnd.lpfnWndProc = MyWndProc;
|
|
wnd.cbClsExtra = 0;
|
|
wnd.cbWndExtra = 0;
|
|
wnd.hInstance = hInstance;
|
|
wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); //default icon
|
|
wnd.hCursor = LoadCursor(NULL, IDC_ARROW); //default arrow mouse cursor
|
|
wnd.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
|
|
wnd.lpszMenuName = NULL; //no menu
|
|
wnd.lpszClassName = szClassName;
|
|
|
|
if(!RegisterClassA(&wnd)) //register the WNDCLASS
|
|
{
|
|
MessageBoxA(NULL, "This Program Requires Windows NT", "Error", MB_OK);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
// CreateWindowA **requires** unicode window name even in non-unicode mode.
|
|
int wlen = strlen( name_of_window );
|
|
char * unicodeao = (char*)alloca( wlen * 2 + 2 );
|
|
int i;
|
|
for( i = 0; i <= wlen; i++ )
|
|
{
|
|
unicodeao[i * 2 + 1] = 0;
|
|
unicodeao[i * 2 + 0] = name_of_window[i];
|
|
}
|
|
name_of_window = unicodeao;
|
|
#endif
|
|
|
|
|
|
CNFGlsHWND = CreateWindowA(szClassName,
|
|
name_of_window, //name_of_window, but must always be
|
|
isFullscreen ? (WS_MAXIMIZE | WS_POPUP) : (WS_OVERLAPPEDWINDOW), //basic window style
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT, //set starting point to default value
|
|
CNFGBufferx,
|
|
CNFGBuffery, //set all the dimensions to default value
|
|
NULL, //no parent window
|
|
NULL, //no menu
|
|
hInstance,
|
|
NULL); //no parameters to pass
|
|
|
|
CNFGlsWindowHDC = GetDC( CNFGlsHWND );
|
|
|
|
#ifdef CNFGOGL
|
|
//From NeHe
|
|
static PIXELFORMATDESCRIPTOR pfd =
|
|
{
|
|
sizeof(PIXELFORMATDESCRIPTOR),
|
|
1,
|
|
PFD_DRAW_TO_WINDOW |
|
|
PFD_SUPPORT_OPENGL |
|
|
PFD_DOUBLEBUFFER,
|
|
PFD_TYPE_RGBA,
|
|
24,
|
|
8, 0, 8, 8, 8, 16,
|
|
8,
|
|
24,
|
|
32,
|
|
8, 8, 8, 8,
|
|
16,
|
|
0,
|
|
0,
|
|
PFD_MAIN_PLANE,
|
|
0,
|
|
0, 0, 0
|
|
};
|
|
GLuint PixelFormat = ChoosePixelFormat( CNFGlsWindowHDC, &pfd );
|
|
if( !SetPixelFormat( CNFGlsWindowHDC, PixelFormat, &pfd ) )
|
|
{
|
|
MessageBoxA( 0, "Could not create PFD for OpenGL Context\n", 0, 0 );
|
|
exit( -1 );
|
|
}
|
|
if (!(hRC=wglCreateContext(CNFGlsWindowHDC))) // Are We Able To Get A Rendering Context?
|
|
{
|
|
MessageBoxA( 0, "Could not create OpenGL Context\n", 0, 0 );
|
|
exit( -1 );
|
|
}
|
|
if(!wglMakeCurrent(CNFGlsWindowHDC,hRC)) // Try To Activate The Rendering Context
|
|
{
|
|
MessageBoxA( 0, "Could not current OpenGL Context\n", 0, 0 );
|
|
exit( -1 );
|
|
}
|
|
#endif
|
|
|
|
CNFGlsHDC = CreateCompatibleDC( CNFGlsWindowHDC );
|
|
CNFGlsHDCBlit = CreateCompatibleDC( CNFGlsWindowHDC );
|
|
CNFGlsBitmap = CreateCompatibleBitmap( CNFGlsWindowHDC, CNFGBufferx, CNFGBuffery );
|
|
SelectObject( CNFGlsHDC, CNFGlsBitmap );
|
|
|
|
//lsClearBrush = CreateSolidBrush( CNFGBGColor );
|
|
//lsHBR = CreateSolidBrush( 0xFFFFFF );
|
|
//lsHPEN = CreatePen( PS_SOLID, 0, 0xFFFFFF );
|
|
|
|
if( show_window )
|
|
ShowWindow(CNFGlsHWND, isFullscreen ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); //display the window on the screen
|
|
|
|
//Once set up... we have to change the window's borders so we get the client size right.
|
|
GetClientRect( CNFGlsHWND, &client );
|
|
GetWindowRect( CNFGlsHWND, &window );
|
|
w = ( window.right - window.left);
|
|
h = ( window.bottom - window.top);
|
|
wd = w - client.right;
|
|
hd = h - client.bottom;
|
|
MoveWindow( CNFGlsHWND, window.left, window.top, CNFGBufferx + wd, CNFGBuffery + hd, 1 );
|
|
|
|
InternalHandleResize();
|
|
|
|
#ifdef CNFG_BATCH
|
|
#ifndef CNFGCONTEXTONLY
|
|
CNFGSetupBatchInternal();
|
|
#endif
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int CNFGHandleInput()
|
|
{
|
|
#ifdef CNFGOGL
|
|
if (ShouldClose)
|
|
exit(0);
|
|
#endif
|
|
|
|
MSG msg;
|
|
while( PeekMessage( &msg, NULL, 0, 0xFFFF, 1 ) )
|
|
{
|
|
TranslateMessage(&msg);
|
|
|
|
switch( msg.message )
|
|
{
|
|
case WM_MOUSEMOVE:
|
|
HandleMotion( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, ( (msg.wParam & 0x01)?1:0) | ((msg.wParam & 0x02)?2:0) | ((msg.wParam & 0x10)?4:0) );
|
|
break;
|
|
case WM_LBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 1 ); break;
|
|
case WM_RBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 1 ); break;
|
|
case WM_MBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 1 ); break;
|
|
case WM_LBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 0 ); break;
|
|
case WM_RBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 0 ); break;
|
|
case WM_MBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 0 ); break;
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
if (msg.lParam & 0x01000000) HandleKey( (int) msg.wParam + 0x7C , (msg.message==WM_KEYDOWN) );
|
|
else HandleKey( (int) msg.wParam, (msg.message==WM_KEYDOWN) );
|
|
break;
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
POINT p = { 0 };
|
|
p.x = LOWORD( msg.lParam );
|
|
p.y = HIWORD( msg.lParam );
|
|
ScreenToClient(CNFGlsHWND, &p);
|
|
HandleButton(p.x, p.y, GET_WHEEL_DELTA_WPARAM(msg.wParam) > 0 ? 0x0E : 0x0F, 1);
|
|
} break;
|
|
default:
|
|
DispatchMessage(&msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return !ShouldClose;
|
|
}
|
|
|
|
#ifndef CNFGOGL
|
|
|
|
#ifndef CNFGRASTERIZER
|
|
|
|
static HBITMAP lsBackBitmap;
|
|
static HBRUSH lsHBR;
|
|
static HPEN lsHPEN;
|
|
static HBRUSH lsClearBrush;
|
|
|
|
static void InternalHandleResize()
|
|
{
|
|
DeleteObject( lsBackBitmap );
|
|
lsBackBitmap = CreateCompatibleBitmap( CNFGlsHDC, CNFGBufferx, CNFGBuffery );
|
|
SelectObject( CNFGlsHDC, lsBackBitmap );
|
|
}
|
|
|
|
#ifdef BATCH_ELEMENTS
|
|
|
|
static int linelisthead;
|
|
static int pointlisthead;
|
|
static int polylisthead;
|
|
static int polylistindex;
|
|
static POINT linelist[4096*3];
|
|
static DWORD twoarray[4096];
|
|
static POINT pointlist[4096];
|
|
static POINT polylist[8192];
|
|
static INT polylistcutoffs[8192];
|
|
|
|
|
|
static int last_linex;
|
|
static int last_liney;
|
|
static int possible_lastline;
|
|
|
|
void FlushTacking()
|
|
{
|
|
int i;
|
|
|
|
if( twoarray[0] != 2 )
|
|
for( i = 0; i < 4096; i++ ) twoarray[i] = 2;
|
|
|
|
if( linelisthead )
|
|
{
|
|
PolyPolyline( CNFGlsHDC, linelist, twoarray, linelisthead );
|
|
linelisthead = 0;
|
|
}
|
|
|
|
if( polylistindex )
|
|
{
|
|
PolyPolygon( CNFGlsHDC, polylist, polylistcutoffs, polylistindex );
|
|
polylistindex = 0;
|
|
polylisthead = 0;
|
|
}
|
|
|
|
if( possible_lastline )
|
|
CNFGTackPixel( last_linex, last_liney );
|
|
possible_lastline = 0;
|
|
|
|
//XXX TODO: Consider locking the bitmap, and manually drawing the pixels.
|
|
if( pointlisthead )
|
|
{
|
|
for( i = 0; i < pointlisthead; i++ )
|
|
{
|
|
SetPixel( CNFGlsHDC, pointlist[i].x, pointlist[i].y, CNFGLastColor );
|
|
}
|
|
pointlisthead = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
uint32_t CNFGColor( uint32_t RGB )
|
|
{
|
|
RGB = COLORSWAPS( RGB );
|
|
if( CNFGLastColor == RGB ) return RGB;
|
|
|
|
#ifdef BATCH_ELEMENTS
|
|
FlushTacking();
|
|
#endif
|
|
|
|
CNFGLastColor = RGB;
|
|
|
|
DeleteObject( lsHBR );
|
|
lsHBR = CreateSolidBrush( RGB );
|
|
SelectObject( CNFGlsHDC, lsHBR );
|
|
|
|
DeleteObject( lsHPEN );
|
|
lsHPEN = CreatePen( PS_SOLID, 0, RGB );
|
|
SelectObject( CNFGlsHDC, lsHPEN );
|
|
|
|
return RGB;
|
|
}
|
|
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
{
|
|
static int pbw, pbh;
|
|
static HBITMAP pbb;
|
|
if( !pbb || pbw != w || pbh !=h )
|
|
{
|
|
if( pbb ) DeleteObject( pbb );
|
|
pbb = CreateBitmap( w, h, 1, 32, 0 );
|
|
pbh = h;
|
|
pbw = w;
|
|
}
|
|
SetBitmapBits(pbb,w*h*4,data);
|
|
SelectObject( CNFGlsHDCBlit, pbb );
|
|
BitBlt(CNFGlsHDC, x, y, w, h, CNFGlsHDCBlit, 0, 0, SRCCOPY);
|
|
}
|
|
|
|
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
|
|
{
|
|
#ifdef BATCH_ELEMENTS
|
|
|
|
if( ( x1 != last_linex || y1 != last_liney ) && possible_lastline )
|
|
{
|
|
CNFGTackPixel( last_linex, last_liney );
|
|
}
|
|
|
|
if( x1 == x2 && y1 == y2 )
|
|
{
|
|
CNFGTackPixel( x1, y1 );
|
|
possible_lastline = 0;
|
|
return;
|
|
}
|
|
|
|
last_linex = x2;
|
|
last_liney = y2;
|
|
possible_lastline = 1;
|
|
|
|
if( x1 != x2 || y1 != y2 )
|
|
{
|
|
linelist[linelisthead*2+0].x = x1;
|
|
linelist[linelisthead*2+0].y = y1;
|
|
linelist[linelisthead*2+1].x = x2;
|
|
linelist[linelisthead*2+1].y = y2;
|
|
linelisthead++;
|
|
if( linelisthead >= 2048 ) FlushTacking();
|
|
}
|
|
#else
|
|
POINT pt[2] = { {x1, y1}, {x2, y2} };
|
|
Polyline( CNFGlsHDC, pt, 2 );
|
|
SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor );
|
|
SetPixel( CNFGlsHDC, x2, y2, CNFGLastColor );
|
|
#endif
|
|
}
|
|
|
|
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
|
|
{
|
|
#ifdef BATCH_ELEMENTS
|
|
FlushTacking();
|
|
#endif
|
|
RECT r;
|
|
if( x1 < x2 ) { r.left = x1; r.right = x2; }
|
|
else { r.left = x2; r.right = x1; }
|
|
if( y1 < y2 ) { r.top = y1; r.bottom = y2; }
|
|
else { r.top = y2; r.bottom = y1; }
|
|
FillRect( CNFGlsHDC, &r, lsHBR );
|
|
}
|
|
|
|
void CNFGClearFrame()
|
|
{
|
|
#ifdef BATCH_ELEMENTS
|
|
FlushTacking();
|
|
#endif
|
|
RECT r = { 0, 0, CNFGBufferx, CNFGBuffery };
|
|
DeleteObject( lsClearBrush );
|
|
lsClearBrush = CreateSolidBrush( COLORSWAPS(CNFGBGColor) );
|
|
HBRUSH prevBrush = SelectObject( CNFGlsHDC, lsClearBrush );
|
|
FillRect( CNFGlsHDC, &r, lsClearBrush);
|
|
SelectObject( CNFGlsHDC, prevBrush );
|
|
}
|
|
|
|
void CNFGTackPoly( RDPoint * points, int verts )
|
|
{
|
|
#ifdef BATCH_ELEMENTS
|
|
if( verts > 8192 )
|
|
{
|
|
FlushTacking();
|
|
//Fall-through
|
|
}
|
|
else
|
|
{
|
|
if( polylistindex >= 8191 || polylisthead + verts >= 8191 )
|
|
{
|
|
FlushTacking();
|
|
}
|
|
int i;
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
polylist[polylisthead].x = points[i].x;
|
|
polylist[polylisthead].y = points[i].y;
|
|
polylisthead++;
|
|
}
|
|
polylistcutoffs[polylistindex++] = verts;
|
|
return;
|
|
}
|
|
#endif
|
|
{
|
|
int i;
|
|
POINT * t = (POINT*)alloca( sizeof( POINT ) * verts );
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
t[i].x = points[i].x;
|
|
t[i].y = points[i].y;
|
|
}
|
|
Polygon( CNFGlsHDC, t, verts );
|
|
}
|
|
}
|
|
|
|
|
|
void CNFGTackPixel( short x1, short y1 )
|
|
{
|
|
#ifdef BATCH_ELEMENTS
|
|
pointlist[pointlisthead+0].x = x1;
|
|
pointlist[pointlisthead+0].y = y1;
|
|
pointlisthead++;
|
|
|
|
if( pointlisthead >=4096 ) FlushTacking();
|
|
#else
|
|
SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor );
|
|
#endif
|
|
|
|
}
|
|
|
|
void CNFGSwapBuffers()
|
|
{
|
|
#ifdef BATCH_ELEMENTS
|
|
FlushTacking();
|
|
#endif
|
|
int thisw, thish;
|
|
|
|
RECT r;
|
|
BitBlt( CNFGlsWindowHDC, 0, 0, CNFGBufferx, CNFGBuffery, CNFGlsHDC, 0, 0, SRCCOPY );
|
|
UpdateWindow( CNFGlsHWND );
|
|
//Check to see if the window is closed.
|
|
if( !IsWindow( CNFGlsHWND ) )
|
|
{
|
|
exit( 0 );
|
|
}
|
|
|
|
GetClientRect( CNFGlsHWND, &r );
|
|
thisw = r.right - r.left;
|
|
thish = r.bottom - r.top;
|
|
|
|
if( thisw != CNFGBufferx || thish != CNFGBuffery )
|
|
{
|
|
CNFGBufferx = (short)thisw;
|
|
CNFGBuffery = (short)thish;
|
|
InternalHandleResize();
|
|
}
|
|
}
|
|
|
|
void CNFGInternalResize( short bfx, short bfy ) { }
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif // _CNFGWINDRIVER_C
|
|
|
|
|
|
#elif defined( EGL_LEAN_AND_MEAN )
|
|
//Copyright (c) 2011, 2017, 2018, 2020 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
|
|
|
|
//This driver cannot create an OpenGL Surface, but can be used for computing for background tasks.
|
|
|
|
//NOTE: This is a truly incomplete driver - if no EGL surface is available, it does not support direct buffer rendering.
|
|
//Additionally no input is connected.
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <GLES3/gl3.h>
|
|
#include <GLES3/gl32.h>
|
|
#include <EGL/egl.h>
|
|
#include <GLES2/gl2.h>
|
|
#include <GLES2/gl2ext.h>
|
|
#include <GLES2/gl2ext.h>
|
|
|
|
|
|
static const EGLint configAttribs[] = {
|
|
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
EGL_RED_SIZE, 8,
|
|
EGL_GREEN_SIZE, 8,
|
|
EGL_BLUE_SIZE, 8,
|
|
EGL_NONE
|
|
};
|
|
|
|
EGLint context_attribs[] = {
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
EGL_NONE
|
|
};
|
|
|
|
static int pbufferWidth = 0;
|
|
static int pbufferHeight = 0;
|
|
|
|
static EGLint pbufferAttribs[] = {
|
|
EGL_WIDTH, 0,
|
|
EGL_HEIGHT, 0,
|
|
EGL_NONE,
|
|
};
|
|
|
|
EGLDisplay eglDpy = 0;
|
|
EGLContext eglCtx = 0;
|
|
EGLSurface eglSurf = 0;
|
|
|
|
void CNFGGetDimensions( short * x, short * y )
|
|
{
|
|
*x = pbufferWidth;
|
|
*y = pbufferHeight;
|
|
}
|
|
|
|
void CNFGChangeWindowTitle( const char * WindowName )
|
|
{
|
|
}
|
|
|
|
void CNFGSetupFullscreen( const char * WindowName, int screen_no )
|
|
{
|
|
//Fullscreen is meaningless for this driver, since it doesn't really open a window.
|
|
CNFGSetup( WindowName, 1024, 1024 );
|
|
}
|
|
|
|
void CNFGTearDown()
|
|
{
|
|
if( eglDpy )
|
|
{
|
|
eglTerminate( eglDpy );
|
|
}
|
|
//Unimplemented.
|
|
}
|
|
|
|
int CNFGSetup( const char * WindowName, int w, int h )
|
|
{
|
|
eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
atexit( CNFGTearDown );
|
|
printf( "EGL Display: %p\n", eglDpy );
|
|
|
|
pbufferAttribs[1] = pbufferWidth = w;
|
|
pbufferAttribs[3] = pbufferHeight = h;
|
|
|
|
EGLint major, minor;
|
|
eglInitialize(eglDpy, &major, &minor);
|
|
|
|
EGLint numConfigs=0;
|
|
EGLConfig eglCfg=NULL;
|
|
|
|
eglChooseConfig(eglDpy, configAttribs, 0, 0, &numConfigs); //this gets number of configs
|
|
if (numConfigs) {
|
|
eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs);
|
|
printf( " EGL config found\n" );
|
|
} else {
|
|
printf( " Error could not find a valid config avail.. \n" );
|
|
}
|
|
|
|
printf( "EGL Major Minor: %d %d\n", major, minor );
|
|
eglBindAPI(EGL_OPENGL_API);
|
|
eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, context_attribs);
|
|
int err = eglGetError(); if(err != EGL_SUCCESS) { printf("1. Error %d\n", err); }
|
|
printf( "EGL Got context: %p\n", eglCtx );
|
|
|
|
if( w > 0 && h > 0 )
|
|
{
|
|
eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs);
|
|
eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx);
|
|
printf( "EGL Current, with surface %p\n", eglSurf );
|
|
//Actually have a surface. Need to allocate it.
|
|
EGLint surfwid;
|
|
EGLint surfht;
|
|
eglQuerySurface(eglDpy, eglSurf, EGL_WIDTH, &surfwid);
|
|
eglQuerySurface(eglDpy, eglSurf, EGL_HEIGHT, &surfht);
|
|
printf("Window dimensions: %d x %d\n", surfwid, surfht);
|
|
}
|
|
else
|
|
{
|
|
eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx);
|
|
printf( "EGL Current, no surface.\n" );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int CNFGHandleInput()
|
|
{
|
|
//Stubbed (No input)
|
|
return 1;
|
|
}
|
|
|
|
void CNFGSetVSync( int vson )
|
|
{
|
|
//No-op
|
|
}
|
|
|
|
void CNFGSwapBuffers()
|
|
{
|
|
//No-op
|
|
}
|
|
|
|
|
|
#elif defined( __android__ ) || defined( ANDROID )
|
|
/*
|
|
* Copyright (c) 2011-2013 Luc Verhaegen <libv@skynet.be>
|
|
* Copyright (c) 2018-2020 <>< Charles Lohr
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sub license,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
* of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#if defined( __android__ ) && !defined( ANDROID )
|
|
#define ANDROID
|
|
#endif
|
|
|
|
//Note: This interface provides the following two things privately.
|
|
//you may "extern" them in your code.
|
|
|
|
|
|
#ifdef ANDROID
|
|
|
|
|
|
#ifndef _CNFG_ANDROID_H
|
|
#define _CNFG_ANDROID_H
|
|
|
|
//This file contains the additional functions that are available on the Android platform.
|
|
//In order to build rawdraw for Android, please compile CNFGEGLDriver.c with -DANDROID
|
|
|
|
// Tricky: Android headers are confused by c++ if linking statically.
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
int __system_property_get(const char* __name, char* __value);
|
|
};
|
|
#endif
|
|
|
|
extern struct android_app * gapp;
|
|
void AndroidMakeFullscreen();
|
|
int AndroidHasPermissions(const char* perm_name);
|
|
void AndroidRequestAppPermissions(const char * perm);
|
|
void AndroidDisplayKeyboard(int pShow);
|
|
int AndroidGetUnicodeChar( int keyCode, int metaState );
|
|
void AndroidSendToBack( int param );
|
|
|
|
extern int android_sdk_version; //Derived at start from property ro.build.version.sdk
|
|
extern int android_width, android_height;
|
|
extern int UpdateScreenWithBitmapOffsetX;
|
|
extern int UpdateScreenWithBitmapOffsetY;
|
|
|
|
// If you need them, these are the names of raw EGL symbols.
|
|
//extern EGLDisplay egl_display;
|
|
//extern EGLSurface egl_surface;
|
|
//extern EGLContext egl_context;
|
|
//extern EGLConfig egl_config;
|
|
|
|
|
|
//You must implement these.
|
|
void HandleResume();
|
|
void HandleSuspend();
|
|
|
|
|
|
//Departures:
|
|
|
|
// HandleMotion's "mask" parameter is actually just an index, not a mask
|
|
|
|
// CNFGSetup / CNFGSetupFullScreen only controls whether or not the navigation
|
|
// decoration is removed. Fullscreen means *full screen* To choose fullscreen
|
|
// or not fullscrene, modify, in your AndroidManifest.xml file, the application
|
|
// section to either contain or not contain:
|
|
// android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
|
|
|
#endif
|
|
|
|
|
|
struct android_app * gapp;
|
|
static int OGLESStarted;
|
|
int android_width, android_height;
|
|
int override_android_screen_dimensons = 0;
|
|
int android_sdk_version;
|
|
|
|
#include <android_native_app_glue.h>
|
|
#include <jni.h>
|
|
#include <android/native_activity.h>
|
|
#define ERRLOG(...) printf( __VA_ARGS__ );
|
|
#else
|
|
#define ERRLOG(...) fprintf( stderr, __VA_ARGS__ );
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include <EGL/egl.h>
|
|
|
|
#ifdef ANDROID
|
|
#include <GLES3/gl3.h>
|
|
#else
|
|
#include <GLES2/gl2.h>
|
|
#endif
|
|
|
|
#define EGL_ZBITS 16
|
|
#define EGL_IMMEDIATE_SIZE 2048
|
|
|
|
#ifdef USE_EGL_X
|
|
#error This feature has never been completed or tested.
|
|
Display *XDisplay;
|
|
Window XWindow;
|
|
#else
|
|
typedef enum
|
|
{
|
|
FBDEV_PIXMAP_DEFAULT = 0,
|
|
FBDEV_PIXMAP_SUPPORTS_UMP = (1<<0),
|
|
FBDEV_PIXMAP_ALPHA_FORMAT_PRE = (1<<1),
|
|
FBDEV_PIXMAP_COLORSPACE_sRGB = (1<<2),
|
|
FBDEV_PIXMAP_EGL_MEMORY = (1<<3) /* EGL allocates/frees this memory */
|
|
} fbdev_pixmap_flags;
|
|
|
|
typedef struct fbdev_window
|
|
{
|
|
unsigned short width;
|
|
unsigned short height;
|
|
} fbdev_window;
|
|
|
|
typedef struct fbdev_pixmap
|
|
{
|
|
unsigned int height;
|
|
unsigned int width;
|
|
unsigned int bytes_per_pixel;
|
|
unsigned char buffer_size;
|
|
unsigned char red_size;
|
|
unsigned char green_size;
|
|
unsigned char blue_size;
|
|
unsigned char alpha_size;
|
|
unsigned char luminance_size;
|
|
fbdev_pixmap_flags flags;
|
|
unsigned short *data;
|
|
unsigned int format; /* extra format information in case rgbal is not enough, especially for YUV formats */
|
|
} fbdev_pixmap;
|
|
|
|
#if defined( ANDROID )
|
|
EGLNativeWindowType native_window;
|
|
#else
|
|
struct fbdev_window native_window;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
static EGLint const config_attribute_list[] = {
|
|
EGL_RED_SIZE, 8,
|
|
EGL_GREEN_SIZE, 8,
|
|
EGL_BLUE_SIZE, 8,
|
|
EGL_ALPHA_SIZE, 8,
|
|
EGL_BUFFER_SIZE, 32,
|
|
EGL_STENCIL_SIZE, 0,
|
|
EGL_DEPTH_SIZE, EGL_ZBITS,
|
|
//EGL_SAMPLES, 1,
|
|
#ifdef ANDROID
|
|
#if ANDROIDVERSION >= 28
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
|
|
#else
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
#endif
|
|
|
|
#else
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PIXMAP_BIT,
|
|
#endif
|
|
EGL_NONE
|
|
};
|
|
|
|
|
|
static EGLint window_attribute_list[] = {
|
|
EGL_NONE
|
|
};
|
|
|
|
static const EGLint context_attribute_list[] = {
|
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
EGL_NONE
|
|
};
|
|
|
|
EGLDisplay egl_display;
|
|
EGLSurface egl_surface;
|
|
EGLContext egl_context;
|
|
EGLConfig egl_config;
|
|
|
|
void CNFGSetVSync( int vson )
|
|
{
|
|
eglSwapInterval(egl_display, vson);
|
|
}
|
|
|
|
static short iLastInternalW, iLastInternalH;
|
|
|
|
void CNFGSwapBuffers()
|
|
{
|
|
CNFGFlushRender();
|
|
eglSwapBuffers(egl_display, egl_surface);
|
|
#ifdef ANDROID
|
|
if( !override_android_screen_dimensons )
|
|
{
|
|
android_width = ANativeWindow_getWidth( native_window );
|
|
android_height = ANativeWindow_getHeight( native_window );
|
|
}
|
|
glViewport( 0, 0, android_width, android_height );
|
|
if( iLastInternalW != android_width || iLastInternalH != android_height )
|
|
CNFGInternalResize( iLastInternalW=android_width, iLastInternalH=android_height );
|
|
#endif
|
|
}
|
|
|
|
void CNFGGetDimensions( short * x, short * y )
|
|
{
|
|
#ifdef ANDROID
|
|
*x = android_width;
|
|
*y = android_height;
|
|
#else
|
|
*x = native_window.width;
|
|
*y = native_window.height;
|
|
#endif
|
|
if( *x != iLastInternalW || *y != iLastInternalH )
|
|
CNFGInternalResize( iLastInternalW=*x, iLastInternalH=*y );
|
|
}
|
|
|
|
int CNFGSetup( const char * WindowName, int w, int h )
|
|
{
|
|
EGLint egl_major, egl_minor;
|
|
EGLint num_config;
|
|
|
|
//This MUST be called before doing any initialization.
|
|
int events;
|
|
while( !OGLESStarted )
|
|
{
|
|
struct android_poll_source* source;
|
|
if (ALooper_pollAll( 0, 0, &events, (void**)&source) >= 0)
|
|
{
|
|
if (source != NULL) source->process(gapp, source);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef USE_EGL_X
|
|
XDisplay = XOpenDisplay(NULL);
|
|
if (!XDisplay) {
|
|
ERRLOG( "Error: failed to open X display.\n");
|
|
return -1;
|
|
}
|
|
|
|
Window XRoot = DefaultRootWindow(XDisplay);
|
|
|
|
XSetWindowAttributes XWinAttr;
|
|
XWinAttr.event_mask = ExposureMask | PointerMotionMask;
|
|
|
|
XWindow = XCreateWindow(XDisplay, XRoot, 0, 0, WIDTH, HEIGHT, 0,
|
|
CopyFromParent, InputOutput,
|
|
CopyFromParent, CWEventMask, &XWinAttr);
|
|
|
|
Atom XWMDeleteMessage =
|
|
XInternAtom(XDisplay, "WM_DELETE_WINDOW", False);
|
|
|
|
XMapWindow(XDisplay, XWindow);
|
|
XStoreName(XDisplay, XWindow, "Mali libs test");
|
|
XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 1);
|
|
|
|
egl_display = eglGetDisplay((EGLNativeDisplayType) XDisplay);
|
|
#else
|
|
|
|
#ifndef ANDROID
|
|
if( w >= 1 && h >= 1 )
|
|
{
|
|
native_window.width = w;
|
|
native_window.height =h;
|
|
}
|
|
#endif
|
|
|
|
egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
#endif
|
|
if (egl_display == EGL_NO_DISPLAY) {
|
|
ERRLOG( "Error: No display found!\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!eglInitialize(egl_display, &egl_major, &egl_minor)) {
|
|
ERRLOG( "Error: eglInitialise failed!\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("EGL Version: \"%s\"\n",
|
|
eglQueryString(egl_display, EGL_VERSION));
|
|
printf("EGL Vendor: \"%s\"\n",
|
|
eglQueryString(egl_display, EGL_VENDOR));
|
|
printf("EGL Extensions: \"%s\"\n",
|
|
eglQueryString(egl_display, EGL_EXTENSIONS));
|
|
|
|
eglChooseConfig(egl_display, config_attribute_list, &egl_config, 1,
|
|
&num_config);
|
|
printf( "Config: %d\n", num_config );
|
|
|
|
printf( "Creating Context\n" );
|
|
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT,
|
|
// NULL );
|
|
context_attribute_list);
|
|
if (egl_context == EGL_NO_CONTEXT) {
|
|
ERRLOG( "Error: eglCreateContext failed: 0x%08X\n",
|
|
eglGetError());
|
|
return -1;
|
|
}
|
|
printf( "Context Created %p\n", egl_context );
|
|
|
|
#ifdef USE_EGL_X
|
|
egl_surface = eglCreateWindowSurface(egl_display, egl_config, XWindow,
|
|
window_attribute_list);
|
|
#else
|
|
|
|
if( native_window && !gapp->window )
|
|
{
|
|
printf( "WARNING: App restarted without a window. Cannot progress.\n" );
|
|
exit( 0 );
|
|
}
|
|
|
|
printf( "Getting Surface %p\n", native_window = gapp->window );
|
|
|
|
if( !native_window )
|
|
{
|
|
printf( "FAULT: Cannot get window\n" );
|
|
return -5;
|
|
}
|
|
|
|
if( w <= 0 || h <= 0 )
|
|
{
|
|
android_width = ANativeWindow_getWidth( native_window );
|
|
android_height = ANativeWindow_getHeight( native_window );
|
|
}
|
|
else
|
|
{
|
|
override_android_screen_dimensons = 1;
|
|
android_width = w;
|
|
android_height = h;
|
|
}
|
|
printf( "Width/Height: %dx%d\n", android_width, android_height );
|
|
egl_surface = eglCreateWindowSurface(egl_display, egl_config,
|
|
#ifdef ANDROID
|
|
gapp->window,
|
|
#else
|
|
(EGLNativeWindowType)&native_window,
|
|
#endif
|
|
window_attribute_list);
|
|
#endif
|
|
printf( "Got Surface: %p\n", egl_surface );
|
|
|
|
if (egl_surface == EGL_NO_SURFACE) {
|
|
ERRLOG( "Error: eglCreateWindowSurface failed: "
|
|
"0x%08X\n", eglGetError());
|
|
return -1;
|
|
}
|
|
|
|
#ifndef ANDROID
|
|
int width, height;
|
|
if (!eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &width) ||
|
|
!eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &height)) {
|
|
ERRLOG( "Error: eglQuerySurface failed: 0x%08X\n",
|
|
eglGetError());
|
|
return -1;
|
|
}
|
|
printf("Surface size: %dx%d\n", width, height);
|
|
|
|
native_window.width = width;
|
|
native_window.height = height;
|
|
#endif
|
|
|
|
if (!eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) {
|
|
ERRLOG( "Error: eglMakeCurrent() failed: 0x%08X\n",
|
|
eglGetError());
|
|
return -1;
|
|
}
|
|
|
|
printf("GL Vendor: \"%s\"\n", glGetString(GL_VENDOR));
|
|
printf("GL Renderer: \"%s\"\n", glGetString(GL_RENDERER));
|
|
printf("GL Version: \"%s\"\n", glGetString(GL_VERSION));
|
|
printf("GL Extensions: \"%s\"\n", glGetString(GL_EXTENSIONS));
|
|
|
|
CNFGSetupBatchInternal();
|
|
|
|
{
|
|
short dummyx, dummyy;
|
|
CNFGGetDimensions( &dummyx, &dummyy );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CNFGSetupFullscreen( const char * WindowName, int screen_number )
|
|
{
|
|
//Removes decoration, must be called before setup.
|
|
AndroidMakeFullscreen();
|
|
|
|
CNFGSetup( WindowName, -1, -1 );
|
|
}
|
|
|
|
int debuga, debugb, debugc;
|
|
|
|
int32_t handle_input(struct android_app* app, AInputEvent* event)
|
|
{
|
|
#ifdef ANDROID
|
|
//Potentially do other things here.
|
|
|
|
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
|
|
{
|
|
int action = AMotionEvent_getAction( event );
|
|
int whichsource = action >> 8;
|
|
action &= AMOTION_EVENT_ACTION_MASK;
|
|
size_t pointerCount = AMotionEvent_getPointerCount(event);
|
|
|
|
for (size_t i = 0; i < pointerCount; ++i)
|
|
{
|
|
int x, y, index;
|
|
x = AMotionEvent_getX(event, i);
|
|
y = AMotionEvent_getY(event, i);
|
|
index = AMotionEvent_getPointerId( event, i );
|
|
|
|
if( action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_DOWN )
|
|
{
|
|
int id = index;
|
|
if( action == AMOTION_EVENT_ACTION_POINTER_DOWN && id != whichsource ) continue;
|
|
HandleButton( x, y, id, 1 );
|
|
ANativeActivity_showSoftInput( gapp->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED );
|
|
}
|
|
else if( action == AMOTION_EVENT_ACTION_POINTER_UP || action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL )
|
|
{
|
|
int id = index;
|
|
if( action == AMOTION_EVENT_ACTION_POINTER_UP && id != whichsource ) continue;
|
|
HandleButton( x, y, id, 0 );
|
|
}
|
|
else if( action == AMOTION_EVENT_ACTION_MOVE )
|
|
{
|
|
HandleMotion( x, y, index );
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
|
|
{
|
|
int code = AKeyEvent_getKeyCode(event);
|
|
#ifdef ANDROID_USE_SCANCODES
|
|
HandleKey( code, AKeyEvent_getAction(event) );
|
|
#else
|
|
int unicode = AndroidGetUnicodeChar( code, AMotionEvent_getMetaState( event ) );
|
|
if( unicode )
|
|
HandleKey( unicode, AKeyEvent_getAction(event) );
|
|
else
|
|
{
|
|
HandleKey( code, !AKeyEvent_getAction(event) );
|
|
return (code == 4)?1:0; //don't override functionality.
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int CNFGHandleInput()
|
|
{
|
|
|
|
#ifdef ANDROID
|
|
int events;
|
|
struct android_poll_source* source;
|
|
while( ALooper_pollAll( 0, 0, &events, (void**)&source) >= 0 )
|
|
{
|
|
if (source != NULL)
|
|
{
|
|
source->process(gapp, source);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_EGL_X
|
|
while (1) {
|
|
XEvent event;
|
|
|
|
XNextEvent(XDisplay, &event);
|
|
|
|
if ((event.type == MotionNotify) ||
|
|
(event.type == Expose))
|
|
Redraw(width, height);
|
|
else if (event.type == ClientMessage) {
|
|
if (event.xclient.data.l[0] == XWMDeleteMessage)
|
|
break;
|
|
}
|
|
}
|
|
XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 0);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
#ifdef ANDROID
|
|
|
|
|
|
void handle_cmd(struct android_app* app, int32_t cmd)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case APP_CMD_DESTROY:
|
|
//This gets called initially after back.
|
|
HandleDestroy();
|
|
ANativeActivity_finish( gapp->activity );
|
|
break;
|
|
case APP_CMD_INIT_WINDOW:
|
|
//When returning from a back button suspension, this isn't called.
|
|
if( !OGLESStarted )
|
|
{
|
|
OGLESStarted = 1;
|
|
printf( "Got start event\n" );
|
|
}
|
|
else
|
|
{
|
|
CNFGSetup( "", -1, -1 );
|
|
HandleResume();
|
|
}
|
|
break;
|
|
//case APP_CMD_TERM_WINDOW:
|
|
//This gets called initially when you click "back"
|
|
//This also gets called when you are brought into standby.
|
|
//Not sure why - callbacks here seem to break stuff.
|
|
// break;
|
|
default:
|
|
printf( "event not handled: %d", cmd);
|
|
}
|
|
}
|
|
|
|
int __system_property_get(const char* name, char* value);
|
|
|
|
void android_main(struct android_app* app)
|
|
{
|
|
int main( int argc, char ** argv );
|
|
char mainptr[5] = { 'm', 'a', 'i', 'n', 0 };
|
|
char * argv[] = { mainptr, 0 };
|
|
|
|
{
|
|
char sdk_ver_str[92];
|
|
int len = __system_property_get("ro.build.version.sdk", sdk_ver_str);
|
|
if( len <= 0 )
|
|
android_sdk_version = 0;
|
|
else
|
|
android_sdk_version = atoi(sdk_ver_str);
|
|
}
|
|
|
|
gapp = app;
|
|
app->onAppCmd = handle_cmd;
|
|
app->onInputEvent = handle_input;
|
|
printf( "Starting with Android SDK Version: %d\n", android_sdk_version );
|
|
main( 1, argv );
|
|
printf( "Main Complete\n" );
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
#define SETUP_FOR_JAVA_CALL \
|
|
JNIEnv * env = 0; \
|
|
JNIEnv ** envptr = &env; \
|
|
JavaVM * jniiptr = gapp->activity->vm; \
|
|
jniiptr->AttachCurrentThread( (JNIEnv**)&env, 0 ); \
|
|
env = (*envptr);
|
|
#define ENVCALL
|
|
#define JAVA_CALL_DETACH jniiptr->DetachCurrentThread();
|
|
#else
|
|
#define SETUP_FOR_JAVA_CALL \
|
|
const struct JNINativeInterface * env = (struct JNINativeInterface*)gapp->activity->env; \
|
|
const struct JNINativeInterface ** envptr = &env; \
|
|
const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; \
|
|
const struct JNIInvokeInterface * jnii = *jniiptr; \
|
|
jnii->AttachCurrentThread( jniiptr, &envptr, NULL); \
|
|
env = (*envptr);
|
|
#define ENVCALL envptr,
|
|
#define JAVA_CALL_DETACH jnii->DetachCurrentThread( jniiptr );
|
|
#endif
|
|
|
|
void AndroidMakeFullscreen()
|
|
{
|
|
//Partially based on https://stackoverflow.com/questions/47507714/how-do-i-enable-full-screen-immersive-mode-for-a-native-activity-ndk-app
|
|
SETUP_FOR_JAVA_CALL
|
|
|
|
//Get android.app.NativeActivity, then get getWindow method handle, returns view.Window type
|
|
jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity");
|
|
jmethodID getWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;");
|
|
jobject window = env->CallObjectMethod( ENVCALL gapp->activity->clazz, getWindow);
|
|
jclass windowClass = env->FindClass( ENVCALL "android/view/Window");
|
|
jmethodID getDecorView = env->GetMethodID( ENVCALL windowClass, "getDecorView", "()Landroid/view/View;");
|
|
jobject decorView = env->CallObjectMethod( ENVCALL window, getDecorView);
|
|
|
|
/*
|
|
jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" );
|
|
const int flag_WindowProp = env->GetStaticIntField( ENVCALL windowClass, env->GetStaticFieldID( ENVCALL windowClass, "FEATURE_NO_TITLE", "I") );
|
|
jmethodID requestWindowFeature = env->GetMethodID( ENVCALL ClassActivity, "requestWindowFeature", "(I)Z" );
|
|
jobject lNativeActivity = gapp->activity->clazz;
|
|
env->CallBooleanMethod( ENVCALL lNativeActivity, requestWindowFeature, flag_WindowProp );
|
|
*/
|
|
|
|
//Get the flag values associated with systemuivisibility
|
|
jclass viewClass = env->FindClass( ENVCALL "android/view/View");
|
|
const int flagLayoutHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION", "I"));
|
|
const int flagLayoutFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN", "I"));
|
|
const int flagLowProfile = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LOW_PROFILE", "I"));
|
|
const int flagHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I"));
|
|
const int flagFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I"));
|
|
const int flagImmersiveSticky = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I"));
|
|
const int flagLayoutStable = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE", "I"));
|
|
jmethodID setSystemUiVisibility = env->GetMethodID( ENVCALL viewClass, "setSystemUiVisibility", "(I)V");
|
|
//Call the decorView.setSystemUiVisibility(FLAGS)
|
|
env->CallVoidMethod( ENVCALL decorView, setSystemUiVisibility,
|
|
(flagLayoutHideNavigation | flagLayoutFullscreen | flagLowProfile | flagHideNavigation | flagFullscreen | flagImmersiveSticky | flagLayoutStable));
|
|
|
|
//now set some more flags associated with layoutmanager -- note the $ in the class path
|
|
//search for api-versions.xml
|
|
//https://android.googlesource.com/platform/development/+/refs/tags/android-9.0.0_r48/sdk/api-versions.xml
|
|
jclass layoutManagerClass = env->FindClass( ENVCALL "android/view/WindowManager$LayoutParams");
|
|
const int flag_WinMan_Fullscreen = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_FULLSCREEN", "I") ));
|
|
const int flag_WinMan_KeepScreenOn = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_KEEP_SCREEN_ON", "I") ));
|
|
const int flag_WinMan_hw_acc = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_HARDWARE_ACCELERATED", "I") ));
|
|
const int flag_WinMan_NoLimits = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_LAYOUT_NO_LIMITS", "I") ));
|
|
// const int flag_WinMan_flag_not_fullscreen = env->GetStaticIntField(layoutManagerClass, (env->GetStaticFieldID(layoutManagerClass, "FLAG_FORCE_NOT_FULLSCREEN", "I") ));
|
|
//call window.addFlags(FLAGS)
|
|
env->CallVoidMethod( ENVCALL window, (env->GetMethodID (ENVCALL windowClass, "addFlags" , "(I)V")), (flag_WinMan_Fullscreen | flag_WinMan_KeepScreenOn | flag_WinMan_hw_acc | flag_WinMan_NoLimits));
|
|
|
|
|
|
|
|
/*
|
|
// Seems to have no impact, and doesn't work with older Android versions.
|
|
jmethodID setDecorFitsSystemWindows = env->GetMethodID( ENVCALL windowClass, "setDecorFitsSystemWindows", "(Z)V");
|
|
env->CallVoidMethod( ENVCALL window, setDecorFitsSystemWindows, JNI_FALSE );
|
|
|
|
// "Immersive Mode" (Since Android 11+)
|
|
jmethodID getWindowInsetsController = env->GetMethodID( ENVCALL viewClass, "getWindowInsetsController", "()Landroid/view/WindowInsetsController;" );
|
|
if( getWindowInsetsController )
|
|
{
|
|
//windowInsetsController.hide(Type.systemBars())
|
|
jobject windowInsetsController = env->CallObjectMethod( ENVCALL decorView, getWindowInsetsController );
|
|
jclass windowInsetsControllerClass = env->FindClass( ENVCALL "android/view/WindowInsetsController" );
|
|
|
|
jclass windowInsetsTypeClass = env->FindClass( ENVCALL "android/view/WindowInsets/Type" );
|
|
jmethodID typeSystemBars = env->GetStaticMethodID( ENVCALL windowInsetsTypeClass, "systemBars", "()I" );
|
|
int systemBarsType = env->CallStaticIntMethod( ENVCALL windowInsetsTypeClass, typeSystemBars );
|
|
|
|
jmethodID hide = env->GetMethodID( ENVCALL windowInsetsControllerClass, "hide", "(I)V" );
|
|
env->CallVoidMethod( ENVCALL windowInsetsController, hide, systemBarsType );
|
|
}
|
|
*/
|
|
|
|
JAVA_CALL_DETACH
|
|
}
|
|
|
|
void AndroidDisplayKeyboard(int pShow)
|
|
{
|
|
//Based on https://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity
|
|
jint lFlags = 0;
|
|
SETUP_FOR_JAVA_CALL
|
|
|
|
jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity");
|
|
|
|
// Retrieves NativeActivity.
|
|
jobject lNativeActivity = gapp->activity->clazz;
|
|
|
|
|
|
// Retrieves Context.INPUT_METHOD_SERVICE.
|
|
jclass ClassContext = env->FindClass( ENVCALL "android/content/Context");
|
|
jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID( ENVCALL ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;" );
|
|
jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField( ENVCALL ClassContext, FieldINPUT_METHOD_SERVICE );
|
|
|
|
// Runs getSystemService(Context.INPUT_METHOD_SERVICE).
|
|
jclass ClassInputMethodManager = env->FindClass( ENVCALL "android/view/inputmethod/InputMethodManager" );
|
|
jmethodID MethodGetSystemService = env->GetMethodID( ENVCALL activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
|
|
jobject lInputMethodManager = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
|
|
|
|
// Runs getWindow().getDecorView().
|
|
jmethodID MethodGetWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;");
|
|
jobject lWindow = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetWindow);
|
|
jclass ClassWindow = env->FindClass( ENVCALL "android/view/Window");
|
|
jmethodID MethodGetDecorView = env->GetMethodID( ENVCALL ClassWindow, "getDecorView", "()Landroid/view/View;");
|
|
jobject lDecorView = env->CallObjectMethod( ENVCALL lWindow, MethodGetDecorView);
|
|
|
|
if (pShow) {
|
|
// Runs lInputMethodManager.showSoftInput(...).
|
|
jmethodID MethodShowSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
|
|
/*jboolean lResult = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags);
|
|
} else {
|
|
// Runs lWindow.getViewToken()
|
|
jclass ClassView = env->FindClass( ENVCALL "android/view/View");
|
|
jmethodID MethodGetWindowToken = env->GetMethodID( ENVCALL ClassView, "getWindowToken", "()Landroid/os/IBinder;");
|
|
jobject lBinder = env->CallObjectMethod( ENVCALL lDecorView, MethodGetWindowToken);
|
|
|
|
// lInputMethodManager.hideSoftInput(...).
|
|
jmethodID MethodHideSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z");
|
|
/*jboolean lRes = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodHideSoftInput, lBinder, lFlags);
|
|
}
|
|
|
|
JAVA_CALL_DETACH
|
|
}
|
|
|
|
int AndroidGetUnicodeChar( int keyCode, int metaState )
|
|
{
|
|
//https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c/43871301
|
|
|
|
int eventType = AKEY_EVENT_ACTION_DOWN;
|
|
|
|
SETUP_FOR_JAVA_CALL
|
|
|
|
//jclass activityClass = env->FindClass( envptr, "android/app/NativeActivity");
|
|
// Retrieves NativeActivity.
|
|
//jobject lNativeActivity = gapp->activity->clazz;
|
|
|
|
jclass class_key_event = env->FindClass( ENVCALL "android/view/KeyEvent");
|
|
int unicodeKey;
|
|
|
|
jmethodID method_get_unicode_char = env->GetMethodID( ENVCALL class_key_event, "getUnicodeChar", "(I)I");
|
|
jmethodID eventConstructor = env->GetMethodID( ENVCALL class_key_event, "<init>", "(II)V");
|
|
jobject eventObj = env->NewObject( ENVCALL class_key_event, eventConstructor, eventType, keyCode);
|
|
|
|
unicodeKey = env->CallIntMethod( ENVCALL eventObj, method_get_unicode_char, metaState );
|
|
|
|
// Finished with the JVM.
|
|
JAVA_CALL_DETACH
|
|
|
|
//printf("Unicode key is: %d", unicodeKey);
|
|
return unicodeKey;
|
|
}
|
|
|
|
|
|
//Based on: https://stackoverflow.com/questions/41820039/jstringjni-to-stdstringc-with-utf8-characters
|
|
#ifdef __cplusplus
|
|
jstring android_permission_name(JNIEnv ** envptr, const char* perm_name)
|
|
#else
|
|
jstring android_permission_name(const struct JNINativeInterface ** envptr, const char* perm_name)
|
|
#endif
|
|
{
|
|
// nested class permission in class android.Manifest,
|
|
// hence android 'slash' Manifest 'dollar' permission
|
|
#ifdef __cplusplus
|
|
JNIEnv * env = *envptr;
|
|
#else
|
|
const struct JNINativeInterface * env = *envptr;
|
|
#endif
|
|
jclass ClassManifestpermission = env->FindClass( ENVCALL "android/Manifest$permission");
|
|
jfieldID lid_PERM = env->GetStaticFieldID( ENVCALL ClassManifestpermission, perm_name, "Ljava/lang/String;" );
|
|
jstring ls_PERM = (jstring)(env->GetStaticObjectField( ENVCALL ClassManifestpermission, lid_PERM ));
|
|
return ls_PERM;
|
|
}
|
|
|
|
/**
|
|
* \brief Tests whether a permission is granted.
|
|
* \param[in] app a pointer to the android app.
|
|
* \param[in] perm_name the name of the permission, e.g.,
|
|
* "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
|
|
* \retval true if the permission is granted.
|
|
* \retval false otherwise.
|
|
* \note Requires Android API level 23 (Marshmallow, May 2015)
|
|
*/
|
|
int AndroidHasPermissions( const char* perm_name)
|
|
{
|
|
struct android_app* app = gapp;
|
|
SETUP_FOR_JAVA_CALL
|
|
|
|
if( android_sdk_version < 23 )
|
|
{
|
|
printf( "Android SDK version %d does not support AndroidHasPermissions\n", android_sdk_version );
|
|
return 1;
|
|
}
|
|
|
|
int result = 0;
|
|
jstring ls_PERM = android_permission_name(envptr, perm_name);
|
|
|
|
jint PERMISSION_GRANTED = (-1);
|
|
|
|
{
|
|
jclass ClassPackageManager = env->FindClass( ENVCALL "android/content/pm/PackageManager" );
|
|
jfieldID lid_PERMISSION_GRANTED = env->GetStaticFieldID( ENVCALL ClassPackageManager, "PERMISSION_GRANTED", "I" );
|
|
PERMISSION_GRANTED = env->GetStaticIntField( ENVCALL ClassPackageManager, lid_PERMISSION_GRANTED );
|
|
}
|
|
{
|
|
jobject activity = app->activity->clazz;
|
|
jclass ClassContext = env->FindClass( ENVCALL "android/content/Context" );
|
|
jmethodID MethodcheckSelfPermission = env->GetMethodID( ENVCALL ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I" );
|
|
jint int_result = env->CallIntMethod( ENVCALL activity, MethodcheckSelfPermission, ls_PERM );
|
|
result = (int_result == PERMISSION_GRANTED);
|
|
}
|
|
|
|
JAVA_CALL_DETACH
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* \brief Query file permissions.
|
|
* \details This opens the system dialog that lets the user
|
|
* grant (or deny) the permission.
|
|
* \param[in] app a pointer to the android app.
|
|
* \note Requires Android API level 23 (Marshmallow, May 2015)
|
|
*/
|
|
void AndroidRequestAppPermissions(const char * perm)
|
|
{
|
|
if( android_sdk_version < 23 )
|
|
{
|
|
printf( "Android SDK version %d does not support AndroidRequestAppPermissions\n",android_sdk_version );
|
|
return;
|
|
}
|
|
|
|
struct android_app* app = gapp;
|
|
SETUP_FOR_JAVA_CALL
|
|
|
|
jobject activity = app->activity->clazz;
|
|
|
|
jobjectArray perm_array = env->NewObjectArray( ENVCALL 1, env->FindClass( ENVCALL "java/lang/String"), env->NewStringUTF( ENVCALL "" ) );
|
|
env->SetObjectArrayElement( ENVCALL perm_array, 0, android_permission_name(envptr, perm ) );
|
|
jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" );
|
|
|
|
jmethodID MethodrequestPermissions = env->GetMethodID( ENVCALL ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V" );
|
|
|
|
// Last arg (0) is just for the callback (that I do not use)
|
|
env->CallVoidMethod( ENVCALL activity, MethodrequestPermissions, perm_array, 0 );
|
|
|
|
JAVA_CALL_DETACH
|
|
}
|
|
|
|
/* Example:
|
|
int hasperm = android_has_permission( "RECORD_AUDIO" );
|
|
if( !hasperm )
|
|
{
|
|
android_request_app_permissions( "RECORD_AUDIO" );
|
|
}
|
|
*/
|
|
|
|
void AndroidSendToBack( int param )
|
|
{
|
|
struct android_app* app = gapp;
|
|
SETUP_FOR_JAVA_CALL
|
|
jobject activity = app->activity->clazz;
|
|
|
|
//_glfmCallJavaMethodWithArgs(jni, gapp->activity->clazz, "moveTaskToBack", "(Z)Z", Boolean, false);
|
|
jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" );
|
|
jmethodID MethodmoveTaskToBack = env->GetMethodID( ENVCALL ClassActivity, "moveTaskToBack", "(Z)Z" );
|
|
env->CallBooleanMethod( ENVCALL activity, MethodmoveTaskToBack, param );
|
|
JAVA_CALL_DETACH
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#else
|
|
//Copyright (c) 2011, 2017, 2018 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
|
|
//portions from
|
|
//http://www.xmission.com/~georgeps/documentation/tutorials/Xlib_Beginner.html
|
|
|
|
//#define HAS_XINERAMA
|
|
//#define CNFG_HAS_XSHAPE
|
|
//#define FULL_SCREEN_STEAL_FOCUS
|
|
|
|
#ifndef _CNFGXDRIVER_C
|
|
#define _CNFGXDRIVER_C
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xos.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/keysym.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
|
|
#ifdef HAS_XINERAMA
|
|
#include <X11/extensions/shape.h>
|
|
#include <X11/extensions/Xinerama.h>
|
|
#endif
|
|
#ifdef CNFG_HAS_XSHAPE
|
|
#include <X11/extensions/shape.h>
|
|
static XGCValues xsval;
|
|
static Pixmap xspixmap;
|
|
static GC xsgc;
|
|
|
|
static int taint_shape;
|
|
static int prepare_xshape;
|
|
static int was_transp;
|
|
|
|
#endif
|
|
|
|
#ifdef CNFG_BATCH
|
|
void CNFGSetupBatchInternal();
|
|
#endif
|
|
|
|
XWindowAttributes CNFGWinAtt;
|
|
XClassHint *CNFGClassHint;
|
|
char * wm_res_name = 0;
|
|
char * wm_res_class = 0;
|
|
Display *CNFGDisplay;
|
|
Window CNFGWindow;
|
|
int CNFGWindowInvisible;
|
|
Pixmap CNFGPixmap;
|
|
GC CNFGGC;
|
|
GC CNFGWindowGC;
|
|
int CNFGDepth;
|
|
int CNFGScreen;
|
|
Visual * CNFGVisual;
|
|
VisualID CNFGVisualID;
|
|
|
|
#ifdef CNFGOGL
|
|
#include <GL/glx.h>
|
|
#include <GL/glxext.h>
|
|
|
|
GLXContext CNFGCtx;
|
|
void * CNFGGetExtension( const char * extname ) { return (void*)glXGetProcAddressARB((const GLubyte *) extname); }
|
|
GLXFBConfig CNFGGLXFBConfig;
|
|
|
|
|
|
void CNFGGLXSetup( )
|
|
{
|
|
int attribs[] = {
|
|
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
|
GLX_DOUBLEBUFFER, True,
|
|
GLX_RED_SIZE, 1,
|
|
GLX_GREEN_SIZE, 1,
|
|
GLX_BLUE_SIZE, 1,
|
|
GLX_DEPTH_SIZE, 1,
|
|
None };
|
|
int elements = 0;
|
|
GLXFBConfig * cfgs = glXChooseFBConfig( CNFGDisplay, CNFGScreen, attribs, &elements );
|
|
if( elements == 0 )
|
|
{
|
|
fprintf( stderr, "Error: could not get valid GLXFBConfig visual.\n" );
|
|
exit( -1 );
|
|
}
|
|
CNFGGLXFBConfig = cfgs[0];
|
|
XVisualInfo * vis = glXGetVisualFromFBConfig( CNFGDisplay, CNFGGLXFBConfig );
|
|
CNFGVisual = vis->visual;
|
|
CNFGVisualID = vis->visualid;
|
|
CNFGDepth = vis->depth;
|
|
CNFGCtx = glXCreateContext( CNFGDisplay, vis, NULL, True );
|
|
}
|
|
|
|
#endif
|
|
|
|
int CNFGX11ForceNoDecoration;
|
|
XImage *xi;
|
|
|
|
int g_x_global_key_state;
|
|
int g_x_global_shift_key;
|
|
|
|
void CNFGSetWindowIconData( int w, int h, uint32_t * data )
|
|
{
|
|
static Atom net_wm_icon;
|
|
static Atom cardinal;
|
|
|
|
if( !net_wm_icon ) net_wm_icon = XInternAtom( CNFGDisplay, "_NET_WM_ICON", False );
|
|
if( !cardinal ) cardinal = XInternAtom( CNFGDisplay, "CARDINAL", False );
|
|
|
|
unsigned long outdata[w*h];
|
|
int i;
|
|
for( i = 0; i < w*h; i++ )
|
|
{
|
|
outdata[i+2] = data[i];
|
|
}
|
|
outdata[0] = w;
|
|
outdata[1] = h;
|
|
XChangeProperty(CNFGDisplay, CNFGWindow, net_wm_icon, cardinal,
|
|
32, PropModeReplace, (const unsigned char*)outdata, 2 + w*h);
|
|
}
|
|
|
|
|
|
#ifdef CNFG_HAS_XSHAPE
|
|
void CNFGPrepareForTransparency() { prepare_xshape = 1; }
|
|
void CNFGDrawToTransparencyMode( int transp )
|
|
{
|
|
static Pixmap BackupCNFGPixmap;
|
|
static GC BackupCNFGGC;
|
|
if( was_transp && ! transp )
|
|
{
|
|
CNFGGC = BackupCNFGGC;
|
|
CNFGPixmap = BackupCNFGPixmap;
|
|
}
|
|
if( !was_transp && transp )
|
|
{
|
|
BackupCNFGPixmap = CNFGPixmap;
|
|
BackupCNFGGC = CNFGGC;
|
|
taint_shape = 1;
|
|
CNFGGC = xsgc;
|
|
CNFGPixmap = xspixmap;
|
|
}
|
|
was_transp = transp;
|
|
}
|
|
void CNFGClearTransparencyLevel()
|
|
{
|
|
taint_shape = 1;
|
|
XSetForeground(CNFGDisplay, xsgc, 0);
|
|
XFillRectangle(CNFGDisplay, xspixmap, xsgc, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height);
|
|
XSetForeground(CNFGDisplay, xsgc, 1);
|
|
}
|
|
#endif
|
|
|
|
int FullScreen = 0;
|
|
|
|
void CNFGGetDimensions( short * x, short * y )
|
|
{
|
|
static int lastx;
|
|
static int lasty;
|
|
|
|
*x = CNFGWinAtt.width;
|
|
*y = CNFGWinAtt.height;
|
|
|
|
if( lastx != *x || lasty != *y )
|
|
{
|
|
lastx = *x;
|
|
lasty = *y;
|
|
#ifndef CNFGCONTEXTONLY
|
|
CNFGInternalResize( lastx, lasty );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void CNFGChangeWindowTitle( const char * WindowName )
|
|
{
|
|
XSetStandardProperties( CNFGDisplay, CNFGWindow, WindowName, 0, 0, 0, 0, 0 );
|
|
}
|
|
|
|
static void InternalLinkScreenAndGo( const char * WindowName )
|
|
{
|
|
XFlush(CNFGDisplay);
|
|
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
|
|
|
|
if( !wm_res_name ) wm_res_name = strdup( "rawdraw" );
|
|
if( !wm_res_class ) wm_res_class = strdup( "rawdraw" );
|
|
|
|
XGetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint );
|
|
if (!CNFGClassHint) {
|
|
CNFGClassHint = XAllocClassHint();
|
|
if (CNFGClassHint) {
|
|
CNFGClassHint->res_name = wm_res_name;
|
|
CNFGClassHint->res_class = wm_res_class;
|
|
XSetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint );
|
|
} else {
|
|
fprintf( stderr, "Failed to allocate XClassHint!\n" );
|
|
}
|
|
} else {
|
|
fprintf( stderr, "Pre-existing XClassHint\n" );
|
|
}
|
|
|
|
XSelectInput (CNFGDisplay, CNFGWindow, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask );
|
|
|
|
|
|
CNFGWindowGC = XCreateGC(CNFGDisplay, CNFGWindow, 0, 0);
|
|
|
|
|
|
if( CNFGX11ForceNoDecoration )
|
|
{
|
|
Atom window_type = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE", False);
|
|
long value = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE_SPLASH", False);
|
|
XChangeProperty(CNFGDisplay, CNFGWindow, window_type,
|
|
XA_ATOM, 32, PropModeReplace, (unsigned char *) &value,1 );
|
|
}
|
|
|
|
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
|
|
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
|
|
XSetLineAttributes(CNFGDisplay, CNFGGC, 1, LineSolid, CapRound, JoinRound);
|
|
CNFGChangeWindowTitle( WindowName );
|
|
if( !CNFGWindowInvisible )
|
|
XMapWindow(CNFGDisplay, CNFGWindow);
|
|
|
|
#ifdef CNFG_HAS_XSHAPE
|
|
if( prepare_xshape )
|
|
{
|
|
xsval.foreground = 1;
|
|
xsval.line_width = 1;
|
|
xsval.line_style = LineSolid;
|
|
xspixmap = XCreatePixmap(CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, 1);
|
|
xsgc = XCreateGC(CNFGDisplay, xspixmap, 0, &xsval);
|
|
XSetLineAttributes(CNFGDisplay, xsgc, 1, LineSolid, CapRound, JoinRound);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CNFGSetupFullscreen( const char * WindowName, int screen_no )
|
|
{
|
|
#ifdef HAS_XINERAMA
|
|
XineramaScreenInfo *screeninfo = NULL;
|
|
int screens;
|
|
int event_basep, error_basep, a, b;
|
|
CNFGDisplay = XOpenDisplay(NULL);
|
|
CNFGScreen = XDefaultScreen(CNFGDisplay);
|
|
int xpos, ypos;
|
|
|
|
if (!XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep)) {
|
|
fprintf( stderr, "X-Server does not support shape extension\n" );
|
|
exit( 1 );
|
|
}
|
|
|
|
CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen);
|
|
CNFGVisualID = 0;
|
|
CNFGWinAtt.depth = DefaultDepth(CNFGDisplay, CNFGScreen);
|
|
|
|
#ifdef CNFGOGL
|
|
CNFGGLXSetup();
|
|
#endif
|
|
|
|
if (XineramaQueryExtension(CNFGDisplay, &a, &b ) &&
|
|
(screeninfo = XineramaQueryScreens(CNFGDisplay, &screens)) &&
|
|
XineramaIsActive(CNFGDisplay) && screen_no >= 0 &&
|
|
screen_no < screens ) {
|
|
|
|
CNFGWinAtt.width = screeninfo[screen_no].width;
|
|
CNFGWinAtt.height = screeninfo[screen_no].height;
|
|
xpos = screeninfo[screen_no].x_org;
|
|
ypos = screeninfo[screen_no].y_org;
|
|
} else
|
|
{
|
|
CNFGWinAtt.width = XDisplayWidth(CNFGDisplay, CNFGScreen);
|
|
CNFGWinAtt.height = XDisplayHeight(CNFGDisplay, CNFGScreen);
|
|
xpos = 0;
|
|
ypos = 0;
|
|
}
|
|
if (screeninfo)
|
|
XFree(screeninfo);
|
|
|
|
|
|
XSetWindowAttributes setwinattr;
|
|
setwinattr.override_redirect = 1;
|
|
setwinattr.save_under = 1;
|
|
#ifdef CNFG_HAS_XSHAPE
|
|
|
|
if (prepare_xshape && !XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep))
|
|
{
|
|
fprintf( stderr, "X-Server does not support shape extension" );
|
|
exit( 1 );
|
|
}
|
|
|
|
setwinattr.event_mask = 0;
|
|
#else
|
|
//This code is probably made irrelevant by the XSetEventMask in InternalLinkScreenAndGo, if this code is not found needed by 2019-12-31, please remove.
|
|
//setwinattr.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask |KeyPressMask |KeyReleaseMask | SubstructureNotifyMask | FocusChangeMask;
|
|
#endif
|
|
setwinattr.border_pixel = 0;
|
|
setwinattr.colormap = XCreateColormap( CNFGDisplay, RootWindow(CNFGDisplay, 0), CNFGVisual, AllocNone);
|
|
|
|
CNFGWindow = XCreateWindow(CNFGDisplay, XRootWindow(CNFGDisplay, CNFGScreen),
|
|
xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height,
|
|
0, CNFGWinAtt.depth, InputOutput, CNFGVisual,
|
|
CWBorderPixel/* | CWEventMask */ | CWOverrideRedirect | CWSaveUnder | CWColormap,
|
|
&setwinattr);
|
|
|
|
FullScreen = 1;
|
|
InternalLinkScreenAndGo( WindowName );
|
|
#ifdef CNFGOGL
|
|
glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx );
|
|
#endif
|
|
#ifdef CNFG_BATCH
|
|
#ifndef CNFGCONTEXTONLY
|
|
CNFGSetupBatchInternal();
|
|
#endif
|
|
#endif
|
|
|
|
#else
|
|
CNFGSetup( WindowName, 640, 480 );
|
|
#endif
|
|
}
|
|
|
|
|
|
void CNFGTearDown()
|
|
{
|
|
HandleDestroy();
|
|
if( xi ) free( xi );
|
|
if ( CNFGClassHint ) XFree( CNFGClassHint );
|
|
if ( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC );
|
|
if ( CNFGWindowGC ) XFreeGC( CNFGDisplay, CNFGWindowGC );
|
|
if ( CNFGDisplay ) XCloseDisplay( CNFGDisplay );
|
|
CNFGDisplay = NULL;
|
|
CNFGWindowGC = CNFGGC = NULL;
|
|
CNFGClassHint = NULL;
|
|
}
|
|
|
|
int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ )
|
|
{
|
|
wm_res_name = wm_res_name_;
|
|
wm_res_class = wm_res_class_;
|
|
return CNFGSetup( WindowName, w, h);
|
|
}
|
|
|
|
int CNFGSetup( const char * WindowName, int w, int h )
|
|
{
|
|
CNFGDisplay = XOpenDisplay(NULL);
|
|
if ( !CNFGDisplay ) {
|
|
fprintf( stderr, "Could not get an X Display.\n%s",
|
|
"Are you in text mode or using SSH without X11-Forwarding?\n" );
|
|
exit( 1 );
|
|
}
|
|
atexit( CNFGTearDown );
|
|
|
|
CNFGScreen = DefaultScreen(CNFGDisplay);
|
|
CNFGDepth = DefaultDepth(CNFGDisplay, CNFGScreen);
|
|
CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen);
|
|
CNFGVisualID = 0;
|
|
Window wnd = DefaultRootWindow( CNFGDisplay );
|
|
|
|
#ifdef CNFGOGL
|
|
CNFGGLXSetup( );
|
|
#endif
|
|
|
|
XSetWindowAttributes attr;
|
|
attr.background_pixel = 0;
|
|
attr.colormap = XCreateColormap( CNFGDisplay, wnd, CNFGVisual, AllocNone);
|
|
if( w > 0 && h > 0 )
|
|
CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr );
|
|
else
|
|
{
|
|
if( w < 0 ) w = -w;
|
|
if( h < 0 ) h = -h;
|
|
CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr );
|
|
CNFGWindowInvisible = 1;
|
|
}
|
|
|
|
InternalLinkScreenAndGo( WindowName );
|
|
|
|
//Not sure of the purpose of this code - if it's still commented out after 2019-12-31 and no one knows why, please delete it.
|
|
Atom WM_DELETE_WINDOW = XInternAtom( CNFGDisplay, "WM_DELETE_WINDOW", False );
|
|
XSetWMProtocols( CNFGDisplay, CNFGWindow, &WM_DELETE_WINDOW, 1 );
|
|
|
|
#ifdef CNFGOGL
|
|
glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx );
|
|
#endif
|
|
|
|
#ifdef CNFG_BATCH
|
|
#ifndef CNFGCONTEXTONLY
|
|
CNFGSetupBatchInternal();
|
|
#endif
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CNFGHandleInput()
|
|
{
|
|
if( !CNFGWindow ) return 0;
|
|
static int ButtonsDown;
|
|
XEvent report;
|
|
|
|
int bKeyDirection = 1;
|
|
while( XPending( CNFGDisplay ) )
|
|
{
|
|
XNextEvent( CNFGDisplay, &report );
|
|
|
|
bKeyDirection = 1;
|
|
switch (report.type)
|
|
{
|
|
case NoExpose:
|
|
break;
|
|
case Expose:
|
|
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
|
|
if( CNFGPixmap ) XFreePixmap( CNFGDisplay, CNFGPixmap );
|
|
CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth );
|
|
if( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC );
|
|
CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0);
|
|
HandleKey( CNFG_X11_EXPOSE, 0 );
|
|
break;
|
|
case KeyRelease:
|
|
{
|
|
bKeyDirection = 0;
|
|
//Tricky - handle key repeats cleanly.
|
|
if( XPending( CNFGDisplay ) )
|
|
{
|
|
XEvent nev;
|
|
XPeekEvent( CNFGDisplay, &nev );
|
|
if (nev.type == KeyPress && nev.xkey.time == report.xkey.time && nev.xkey.keycode == report.xkey.keycode )
|
|
bKeyDirection = 2;
|
|
}
|
|
}
|
|
case KeyPress:
|
|
g_x_global_key_state = report.xkey.state;
|
|
g_x_global_shift_key = XLookupKeysym(&report.xkey, 1);
|
|
HandleKey( XLookupKeysym(&report.xkey, 0), bKeyDirection );
|
|
break;
|
|
case ButtonRelease:
|
|
bKeyDirection = 0;
|
|
case ButtonPress:
|
|
HandleButton( report.xbutton.x, report.xbutton.y, report.xbutton.button, bKeyDirection );
|
|
ButtonsDown = (ButtonsDown & (~(1<<report.xbutton.button))) | ( bKeyDirection << report.xbutton.button );
|
|
|
|
//Intentionall fall through -- we want to send a motion in event of a button as well.
|
|
case MotionNotify:
|
|
HandleMotion( report.xmotion.x, report.xmotion.y, ButtonsDown>>1 );
|
|
break;
|
|
case ClientMessage:
|
|
// Only subscribed to WM_DELETE_WINDOW, so return 0 to let user know of window exit
|
|
return 0;
|
|
break;
|
|
default:
|
|
break;
|
|
//printf( "Event: %d\n", report.type );
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
#ifdef CNFGOGL
|
|
|
|
void CNFGSetVSync( int vson )
|
|
{
|
|
void (*glfn)( int );
|
|
glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalMESA" ); if( glfn ) glfn( vson );
|
|
glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalSGI" ); if( glfn ) glfn( vson );
|
|
glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalEXT" ); if( glfn ) glfn( vson );
|
|
}
|
|
|
|
#ifdef CNFGRASTERIZER
|
|
void CNFGSwapBuffersInternal()
|
|
#else
|
|
void CNFGSwapBuffers()
|
|
#endif
|
|
{
|
|
if( CNFGWindowInvisible ) return;
|
|
|
|
#ifndef CNFGRASTERIZER
|
|
#ifndef CNFGCONTEXTONLY
|
|
CNFGFlushRender();
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CNFG_HAS_XSHAPE
|
|
if( taint_shape )
|
|
{
|
|
XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet);
|
|
taint_shape = 0;
|
|
}
|
|
#endif //CNFG_HAS_XSHAPE
|
|
glXSwapBuffers( CNFGDisplay, CNFGWindow );
|
|
|
|
#ifdef FULL_SCREEN_STEAL_FOCUS
|
|
if( FullScreen )
|
|
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
|
|
#endif //FULL_SCREEN_STEAL_FOCUS
|
|
}
|
|
|
|
#else //CNFGOGL
|
|
|
|
#ifndef CNFGRASTERIZER
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
{
|
|
static int lw, lh;
|
|
|
|
if( lw != w || lh != h || !xi )
|
|
{
|
|
if( xi ) free( xi );
|
|
xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
|
|
lw = w;
|
|
lh = h;
|
|
}
|
|
//Draw image to pixmap (not a screen flip)
|
|
XPutImage(CNFGDisplay, CNFGPixmap, CNFGGC, xi, 0, 0, x, y, w, h );
|
|
}
|
|
#endif
|
|
|
|
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
|
|
{
|
|
static int lw, lh;
|
|
|
|
if( lw != w || lh != h )
|
|
{
|
|
if( xi ) free( xi );
|
|
xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 );
|
|
lw = w;
|
|
lh = h;
|
|
}
|
|
|
|
//Directly write image to screen (effectively a flip)
|
|
XPutImage(CNFGDisplay, CNFGWindow, CNFGWindowGC, xi, 0, 0, 0, 0, w, h );
|
|
}
|
|
|
|
#endif //CNFGOGL
|
|
|
|
#if !defined( CNFGOGL)
|
|
#define AGLF(x) x
|
|
#else
|
|
#define AGLF(x) static inline BACKEND_##x
|
|
#endif
|
|
|
|
#if defined( CNFGRASTERIZER )
|
|
//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin.
|
|
|
|
#ifdef CNFGRASTERIZER
|
|
//#include <stdlib.h>
|
|
#include <stdint.h>
|
|
|
|
uint32_t * CNFGBuffer = 0;
|
|
short CNFGBufferx;
|
|
short CNFGBuffery;
|
|
|
|
#ifdef CNFGOGL
|
|
void CNFGFlushRender()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
void CNFGInternalResize( short x, short y )
|
|
{
|
|
CNFGBufferx = x;
|
|
CNFGBuffery = y;
|
|
if( CNFGBuffer ) free( CNFGBuffer );
|
|
CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 );
|
|
#ifdef CNFGOGL
|
|
void CNFGInternalResizeOGLBACKEND( short w, short h );
|
|
CNFGInternalResizeOGLBACKEND( x, y );
|
|
#endif
|
|
}
|
|
|
|
#ifdef __wasm__
|
|
static uint32_t SWAPS( uint32_t r )
|
|
{
|
|
uint32_t ret = (r&0xFF)<<24;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<16;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<8;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<0;
|
|
return ret;
|
|
}
|
|
#elif !defined(CNFGOGL)
|
|
#define SWAPS(x) (x>>8)
|
|
#else
|
|
static uint32_t SWAPS( uint32_t r )
|
|
{
|
|
uint32_t ret = (r&0xFF)<<16;
|
|
r>>=8;
|
|
ret |= (r&0xff)<<8;
|
|
r>>=8;
|
|
ret |= (r&0xff);
|
|
r>>=8;
|
|
ret |= (r&0xff)<<24;
|
|
return ret;
|
|
}
|
|
#endif
|
|
uint32_t CNFGColor( uint32_t RGB )
|
|
{
|
|
CNFGLastColor = SWAPS(RGB);
|
|
return CNFGLastColor;
|
|
}
|
|
|
|
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
|
|
{
|
|
short tx, ty;
|
|
//float slope, lp;
|
|
float slope;
|
|
|
|
short dx = x2 - x1;
|
|
short dy = y2 - y1;
|
|
|
|
if( !CNFGBuffer ) return;
|
|
|
|
if( dx < 0 ) dx = -dx;
|
|
if( dy < 0 ) dy = -dy;
|
|
|
|
if( dx > dy )
|
|
{
|
|
short minx = (x1 < x2)?x1:x2;
|
|
short maxx = (x1 < x2)?x2:x1;
|
|
short miny = (x1 < x2)?y1:y2;
|
|
short maxy = (x1 < x2)?y2:y1;
|
|
float thisy = miny;
|
|
slope = (float)(maxy-miny) / (float)(maxx-minx);
|
|
|
|
for( tx = minx; tx <= maxx; tx++ )
|
|
{
|
|
ty = thisy;
|
|
if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue;
|
|
if( tx >= CNFGBufferx ) break;
|
|
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
|
|
thisy += slope;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
short minx = (y1 < y2)?x1:x2;
|
|
short maxx = (y1 < y2)?x2:x1;
|
|
short miny = (y1 < y2)?y1:y2;
|
|
short maxy = (y1 < y2)?y2:y1;
|
|
float thisx = minx;
|
|
slope = (float)(maxx-minx) / (float)(maxy-miny);
|
|
|
|
for( ty = miny; ty <= maxy; ty++ )
|
|
{
|
|
tx = thisx;
|
|
if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue;
|
|
if( ty >= CNFGBuffery ) break;
|
|
CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor;
|
|
thisx += slope;
|
|
}
|
|
}
|
|
}
|
|
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
|
|
{
|
|
short minx = (x1<x2)?x1:x2;
|
|
short miny = (y1<y2)?y1:y2;
|
|
short maxx = (x1>=x2)?x1:x2;
|
|
short maxy = (y1>=y2)?y1:y2;
|
|
|
|
short x, y;
|
|
|
|
if( minx < 0 ) minx = 0;
|
|
if( miny < 0 ) miny = 0;
|
|
if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1;
|
|
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
|
|
|
|
for( y = miny; y <= maxy; y++ )
|
|
{
|
|
uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx];
|
|
for( x = minx; x <= maxx; x++ )
|
|
{
|
|
(*CNFGBufferstart++) = CNFGLastColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CNFGTackPoly( RDPoint * points, int verts )
|
|
{
|
|
short minx = 10000, miny = 10000;
|
|
short maxx =-10000, maxy =-10000;
|
|
short i, x, y;
|
|
|
|
//Just in case...
|
|
if( verts > 32767 ) return;
|
|
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
RDPoint * p = &points[i];
|
|
if( p->x < minx ) minx = p->x;
|
|
if( p->y < miny ) miny = p->y;
|
|
if( p->x > maxx ) maxx = p->x;
|
|
if( p->y > maxy ) maxy = p->y;
|
|
}
|
|
|
|
if( miny < 0 ) miny = 0;
|
|
if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1;
|
|
|
|
for( y = miny; y <= maxy; y++ )
|
|
{
|
|
short startfillx = maxx;
|
|
short endfillx = minx;
|
|
|
|
//Figure out what line segments intersect this line.
|
|
for( i = 0; i < verts; i++ )
|
|
{
|
|
short pl = i + 1;
|
|
if( pl == verts ) pl = 0;
|
|
|
|
RDPoint ptop;
|
|
RDPoint pbot;
|
|
|
|
ptop.x = points[i].x;
|
|
ptop.y = points[i].y;
|
|
pbot.x = points[pl].x;
|
|
pbot.y = points[pl].y;
|
|
//printf( "Poly: %d %d\n", pbot.y, ptop.y );
|
|
|
|
if( pbot.y < ptop.y )
|
|
{
|
|
RDPoint ptmp;
|
|
ptmp.x = pbot.x;
|
|
ptmp.y = pbot.y;
|
|
pbot.x = ptop.x;
|
|
pbot.y = ptop.y;
|
|
ptop.x = ptmp.x;
|
|
ptop.y = ptmp.y;
|
|
}
|
|
|
|
//Make sure this line segment is within our range.
|
|
//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y );
|
|
if( ptop.y <= y && pbot.y >= y )
|
|
{
|
|
short diffy = pbot.y - ptop.y;
|
|
uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math.
|
|
short diffx = pbot.x - ptop.x;
|
|
short isectx;
|
|
|
|
if( diffy == 0 )
|
|
{
|
|
if( pbot.x < ptop.x )
|
|
{
|
|
if( startfillx > pbot.x ) startfillx = pbot.x;
|
|
if( endfillx < ptop.x ) endfillx = ptop.x;
|
|
}
|
|
else
|
|
{
|
|
if( startfillx > ptop.x ) startfillx = ptop.x;
|
|
if( endfillx < pbot.x ) endfillx = pbot.x;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Inner part is scaled by 65536, outer part must be scaled back.
|
|
isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x;
|
|
if( isectx < startfillx ) startfillx = isectx;
|
|
if( isectx > endfillx ) endfillx = isectx;
|
|
}
|
|
//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx );
|
|
}
|
|
}
|
|
|
|
//printf( "%d %d %d\n", y, startfillx, endfillx );
|
|
|
|
if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1;
|
|
if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1;
|
|
if( startfillx < 0 ) startfillx = 0;
|
|
if( startfillx < 0 ) startfillx = 0;
|
|
|
|
unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx];
|
|
for( x = startfillx; x <= endfillx; x++ )
|
|
{
|
|
(*bufferstart++) = CNFGLastColor;
|
|
}
|
|
}
|
|
//exit(1);
|
|
}
|
|
|
|
|
|
void CNFGClearFrame()
|
|
{
|
|
int i, m;
|
|
uint32_t col = 0;
|
|
short x, y;
|
|
CNFGGetDimensions( &x, &y );
|
|
if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer )
|
|
{
|
|
CNFGBufferx = x;
|
|
CNFGBuffery = y;
|
|
CNFGBuffer = malloc( x * y * 8 );
|
|
}
|
|
|
|
m = x * y;
|
|
col = CNFGColor( CNFGBGColor );
|
|
for( i = 0; i < m; i++ )
|
|
{
|
|
CNFGBuffer[i] = col;
|
|
}
|
|
}
|
|
|
|
void CNFGTackPixel( short x, short y )
|
|
{
|
|
if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
|
|
CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor;
|
|
}
|
|
|
|
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
{
|
|
int ox = x;
|
|
int stride = w;
|
|
if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return;
|
|
if( x < 0 ) { w += x; x = 0; }
|
|
if( y < 0 ) { h += y; y = 0; }
|
|
|
|
//Switch w,h to x2, y2
|
|
h += y;
|
|
w += x;
|
|
|
|
if( w >= CNFGBufferx ) { w = CNFGBufferx; }
|
|
if( h >= CNFGBuffery ) { h = CNFGBuffery; }
|
|
|
|
|
|
for( ; y < h-1; y++ )
|
|
{
|
|
x = ox;
|
|
uint32_t * indat = data;
|
|
uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x;
|
|
for( ; x < w-1; x++ )
|
|
{
|
|
uint32_t newm = *(indat++);
|
|
uint32_t oldm = *(outdat);
|
|
if( (newm & 0xff) == 0xff )
|
|
{
|
|
*(outdat++) = newm;
|
|
}
|
|
else
|
|
{
|
|
//Alpha blend.
|
|
int alfa = newm&0xff;
|
|
int onemalfa = 255-alfa;
|
|
#ifdef __wasm__
|
|
uint32_t newv = 255<<0; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
uint32_t newv = 255UL<<24; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
#elif defined( ANDROID ) || defined( __android__ )
|
|
uint32_t newv = 255<<16; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#elif defined( CNFGOGL ) //OGL, on X11
|
|
uint32_t newv = 255<<16; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
#else //X11
|
|
uint32_t newv = 255UL<<24; //Alpha, then RGB
|
|
newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16;
|
|
newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8;
|
|
newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0;
|
|
#endif
|
|
*(outdat++) = newv;
|
|
}
|
|
}
|
|
data += stride;
|
|
}
|
|
}
|
|
|
|
void CNFGSwapBuffers()
|
|
{
|
|
CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery );
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
#undef AGLF
|
|
#define AGLF(x) static inline BACKEND_##x
|
|
#endif
|
|
|
|
uint32_t AGLF(CNFGColor)( uint32_t RGB )
|
|
{
|
|
CNFGLastColor = RGB;
|
|
unsigned char red = ( RGB >> 24 ) & 0xFF;
|
|
unsigned char grn = ( RGB >> 16 ) & 0xFF;
|
|
unsigned char blu = ( RGB >> 8 ) & 0xFF;
|
|
unsigned long color = (red<<16)|(grn<<8)|(blu);
|
|
XSetForeground(CNFGDisplay, CNFGGC, color);
|
|
return color;
|
|
}
|
|
|
|
void AGLF(CNFGClearFrame)()
|
|
{
|
|
XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt );
|
|
XSetForeground(CNFGDisplay, CNFGGC, CNFGColor(CNFGBGColor) );
|
|
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height );
|
|
}
|
|
|
|
void AGLF(CNFGSwapBuffers)()
|
|
{
|
|
#ifdef CNFG_HAS_XSHAPE
|
|
if( taint_shape )
|
|
{
|
|
XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet);
|
|
taint_shape = 0;
|
|
}
|
|
#endif
|
|
XCopyArea(CNFGDisplay, CNFGPixmap, CNFGWindow, CNFGWindowGC, 0,0,CNFGWinAtt.width,CNFGWinAtt.height,0,0);
|
|
XFlush(CNFGDisplay);
|
|
#ifdef FULL_SCREEN_STEAL_FOCUS
|
|
if( FullScreen )
|
|
XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime );
|
|
#endif
|
|
}
|
|
|
|
void AGLF(CNFGTackSegment)( short x1, short y1, short x2, short y2 )
|
|
{
|
|
if( x1 == x2 && y1 == y2 )
|
|
{
|
|
//On some targets, zero-length lines will not show up.
|
|
//This is tricky - since this will also cause more draw calls for points on systems like GLAMOR.
|
|
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2 );
|
|
}
|
|
else
|
|
{
|
|
//XXX HACK! See discussion here: https://github.com/cntools/cnping/issues/68
|
|
XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2, y2 );
|
|
XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2, x1, y1 );
|
|
}
|
|
}
|
|
|
|
void AGLF(CNFGTackPixel)( short x1, short y1 )
|
|
{
|
|
XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1 );
|
|
}
|
|
|
|
void AGLF(CNFGTackRectangle)( short x1, short y1, short x2, short y2 )
|
|
{
|
|
XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2-x1, y2-y1 );
|
|
}
|
|
|
|
void AGLF(CNFGTackPoly)( RDPoint * points, int verts )
|
|
{
|
|
XFillPolygon(CNFGDisplay, CNFGPixmap, CNFGGC, (XPoint *)points, verts, Convex, CoordModeOrigin );
|
|
}
|
|
|
|
void AGLF(CNFGInternalResize)( short x, short y ) { }
|
|
|
|
void AGLF(CNFGSetLineWidth)( short width )
|
|
{
|
|
XSetLineAttributes(CNFGDisplay, CNFGGC, width, LineSolid, CapRound, JoinRound);
|
|
}
|
|
|
|
#endif // _CNFGXDRIVER_C
|
|
|
|
|
|
#endif
|
|
|
|
/*
|
|
Copyright (c) 2010-2021 <>< Charles Lohr, and several others!
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#ifndef _CNFG_C
|
|
#define _CNFG_C
|
|
|
|
|
|
#ifndef CNFGHTTPSERVERONLY
|
|
|
|
#ifdef _CNFG_FANCYFONT
|
|
#include "TextTool/FontData.h"
|
|
#endif
|
|
|
|
//TODO: Refactor to remove malloc reliance.
|
|
#ifndef __clang__
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
int CNFGPenX, CNFGPenY;
|
|
uint32_t CNFGBGColor;
|
|
uint32_t CNFGLastColor;
|
|
//uint32_t CNFGDialogColor; //background for boxes [DEPRECATED]
|
|
|
|
// The following two arrays are generated by Fonter/fonter.cpp
|
|
const unsigned short RawdrawFontCharMap[256] = {
|
|
65535, 0, 8, 16, 24, 31, 41, 50, 51, 65535, 65535, 57, 66, 65535, 75, 83,
|
|
92, 96, 100, 108, 114, 123, 132, 137, 147, 152, 158, 163, 169, 172, 178, 182,
|
|
65535, 186, 189, 193, 201, 209, 217, 226, 228, 232, 236, 244, 248, 250, 252, 253,
|
|
255, 261, 266, 272, 278, 283, 289, 295, 300, 309, 316, 318, 321, 324, 328, 331,
|
|
337, 345, 352, 362, 368, 375, 382, 388, 396, 402, 408, 413, 422, 425, 430, 435,
|
|
442, 449, 458, 466, 472, 476, 480, 485, 492, 500, 507, 512, 516, 518, 522, 525,
|
|
527, 529, 536, 541, 546, 551, 557, 564, 572, 578, 581, 586, 593, 595, 604, 610,
|
|
615, 621, 627, 632, 638, 642, 648, 653, 660, 664, 670, 674, 680, 684, 690, 694,
|
|
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
|
|
65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535,
|
|
700, 703, 711, 718, 731, 740, 744, 754, 756, 760, 766, 772, 775, 777, 785, 787,
|
|
792, 798, 803, 811, 813, 820, 827, 828, 831, 833, 838, 844, 853, 862, 874, 880,
|
|
889, 898, 908, 919, 928, 939, 951, 960, 969, 978, 988, 997, 1005, 1013, 1022, 1030,
|
|
1039, 1047, 1054, 1061, 1070, 1079, 1086, 1090, 1099, 1105, 1111, 1118, 1124, 1133, 1140, 1150,
|
|
1159, 1168, 1178, 1189, 1198, 1209, 1222, 1231, 1239, 1247, 1256, 1264, 1268, 1272, 1277, 1281,
|
|
1290, 1300, 1307, 1314, 1322, 1331, 1338, 1342, 1349, 1357, 1365, 1374, 1382, 1390, 1397, 65535, };
|
|
|
|
const unsigned char RawdrawFontCharData[1405] = {
|
|
0x00, 0x09, 0x20, 0x29, 0x03, 0x23, 0x14, 0x8b, 0x00, 0x09, 0x20, 0x29, 0x04, 0x24, 0x13, 0x8c,
|
|
0x01, 0x21, 0x23, 0x14, 0x03, 0x09, 0x11, 0x9a, 0x11, 0x22, 0x23, 0x14, 0x03, 0x02, 0x99, 0x01,
|
|
0x21, 0x23, 0x09, 0x03, 0x29, 0x03, 0x09, 0x12, 0x9c, 0x03, 0x2b, 0x13, 0x1c, 0x23, 0x22, 0x11,
|
|
0x02, 0x8b, 0x9a, 0x1a, 0x01, 0x21, 0x23, 0x03, 0x89, 0x03, 0x21, 0x2a, 0x21, 0x19, 0x03, 0x14,
|
|
0x23, 0x9a, 0x01, 0x10, 0x21, 0x12, 0x09, 0x12, 0x1c, 0x03, 0xab, 0x02, 0x03, 0x1b, 0x02, 0x1a,
|
|
0x13, 0x10, 0xa9, 0x01, 0x2b, 0x03, 0x29, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x22, 0x04, 0x88,
|
|
0x20, 0x02, 0x24, 0xa8, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14, 0xab, 0x00, 0x0b, 0x0c, 0x20,
|
|
0x2b, 0xac, 0x00, 0x28, 0x00, 0x02, 0x2a, 0x10, 0x1c, 0x20, 0xac, 0x01, 0x21, 0x23, 0x03, 0x09,
|
|
0x20, 0x10, 0x14, 0x8c, 0x03, 0x23, 0x24, 0x04, 0x8b, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14,
|
|
0x2b, 0x04, 0xac, 0x01, 0x18, 0x21, 0x10, 0x9c, 0x03, 0x1c, 0x23, 0x1c, 0x10, 0x9c, 0x02, 0x22,
|
|
0x19, 0x22, 0x9b, 0x02, 0x2a, 0x02, 0x19, 0x02, 0x9b, 0x01, 0x02, 0xaa, 0x02, 0x22, 0x11, 0x02,
|
|
0x13, 0xaa, 0x11, 0x22, 0x02, 0x99, 0x02, 0x13, 0x22, 0x8a, 0x10, 0x1b, 0x9c, 0x10, 0x09, 0x20,
|
|
0x99, 0x10, 0x1c, 0x20, 0x2c, 0x01, 0x29, 0x03, 0xab, 0x21, 0x10, 0x01, 0x23, 0x14, 0x0b, 0x10,
|
|
0x9c, 0x00, 0x09, 0x23, 0x2c, 0x04, 0x03, 0x21, 0xa8, 0x21, 0x10, 0x01, 0x12, 0x03, 0x14, 0x2b,
|
|
0x02, 0xac, 0x10, 0x99, 0x10, 0x01, 0x03, 0x9c, 0x10, 0x21, 0x23, 0x9c, 0x01, 0x2b, 0x11, 0x1b,
|
|
0x21, 0x0b, 0x02, 0xaa, 0x02, 0x2a, 0x11, 0x9b, 0x04, 0x9b, 0x02, 0xaa, 0x9c, 0x03, 0xa9, 0x00,
|
|
0x20, 0x24, 0x04, 0x08, 0x9a, 0x01, 0x10, 0x1c, 0x04, 0xac, 0x01, 0x10, 0x21, 0x22, 0x04, 0xac,
|
|
0x00, 0x20, 0x24, 0x0c, 0x12, 0xaa, 0x00, 0x02, 0x2a, 0x20, 0xac, 0x20, 0x00, 0x02, 0x22, 0x24,
|
|
0x8c, 0x20, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x00, 0x20, 0x21, 0x12, 0x9c, 0x00, 0x0c, 0x00, 0x20,
|
|
0x2c, 0x04, 0x2c, 0x02, 0xaa, 0x00, 0x02, 0x22, 0x20, 0x08, 0x22, 0x8c, 0x19, 0x9b, 0x19, 0x13,
|
|
0x8c, 0x20, 0x02, 0xac, 0x01, 0x29, 0x03, 0xab, 0x00, 0x22, 0x8c, 0x01, 0x10, 0x21, 0x12, 0x1b,
|
|
0x9c, 0x21, 0x01, 0x04, 0x24, 0x22, 0x12, 0x13, 0xab, 0x04, 0x01, 0x10, 0x21, 0x2c, 0x02, 0xaa,
|
|
0x00, 0x04, 0x14, 0x23, 0x12, 0x0a, 0x12, 0x21, 0x10, 0x88, 0x23, 0x14, 0x03, 0x01, 0x10, 0xa9,
|
|
0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x88, 0x00, 0x04, 0x2c, 0x00, 0x28, 0x02, 0x9a, 0x00, 0x0c,
|
|
0x00, 0x28, 0x02, 0x9a, 0x21, 0x10, 0x01, 0x03, 0x14, 0x23, 0x22, 0x9a, 0x00, 0x0c, 0x20, 0x2c,
|
|
0x02, 0xaa, 0x00, 0x28, 0x10, 0x1c, 0x04, 0xac, 0x00, 0x20, 0x23, 0x14, 0x8b, 0x00, 0x0c, 0x02,
|
|
0x12, 0x21, 0x28, 0x12, 0x23, 0xac, 0x00, 0x04, 0xac, 0x04, 0x00, 0x11, 0x20, 0xac, 0x04, 0x00,
|
|
0x2a, 0x20, 0xac, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x89, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12,
|
|
0x8a, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x04, 0x9b, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12,
|
|
0x02, 0xac, 0x21, 0x10, 0x01, 0x23, 0x14, 0x8b, 0x00, 0x28, 0x10, 0x9c, 0x00, 0x04, 0x24, 0xa8,
|
|
0x00, 0x03, 0x14, 0x23, 0xa8, 0x00, 0x04, 0x2c, 0x14, 0x1b, 0x24, 0xa8, 0x00, 0x01, 0x23, 0x2c,
|
|
0x04, 0x03, 0x21, 0xa8, 0x00, 0x01, 0x12, 0x1c, 0x12, 0x21, 0xa8, 0x00, 0x20, 0x02, 0x04, 0xac,
|
|
0x10, 0x00, 0x04, 0x9c, 0x01, 0xab, 0x10, 0x20, 0x24, 0x9c, 0x01, 0x10, 0xa9, 0x04, 0xac, 0x00,
|
|
0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x8a, 0x00, 0x04, 0x24, 0x22, 0x8a, 0x24, 0x04, 0x03,
|
|
0x12, 0xaa, 0x20, 0x24, 0x04, 0x02, 0xaa, 0x24, 0x04, 0x02, 0x22, 0x23, 0x9b, 0x04, 0x09, 0x02,
|
|
0x1a, 0x01, 0x10, 0xa9, 0x23, 0x12, 0x03, 0x14, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x12,
|
|
0x23, 0xac, 0x19, 0x12, 0x9c, 0x2a, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x13, 0x2a, 0x13,
|
|
0xac, 0x10, 0x9c, 0x02, 0x0c, 0x02, 0x1b, 0x12, 0x1c, 0x12, 0x23, 0xac, 0x02, 0x0c, 0x03, 0x12,
|
|
0x23, 0xac, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0d, 0x04, 0x24, 0x22, 0x8a, 0x02, 0x04, 0x2c,
|
|
0x25, 0x22, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0xaa, 0x22, 0x02, 0x03, 0x23, 0x24, 0x8c, 0x11, 0x1c,
|
|
0x02, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0xaa, 0x02, 0x03, 0x14, 0x23, 0xaa, 0x02, 0x03, 0x14,
|
|
0x1a, 0x13, 0x24, 0xaa, 0x02, 0x2c, 0x04, 0xaa, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x8d, 0x02, 0x22,
|
|
0x04, 0xac, 0x20, 0x10, 0x14, 0x2c, 0x12, 0x8a, 0x10, 0x19, 0x13, 0x9c, 0x00, 0x10, 0x14, 0x0c,
|
|
0x12, 0xaa, 0x01, 0x10, 0x11, 0xa8, 0x03, 0x04, 0x24, 0x23, 0x12, 0x8b, 0x18, 0x11, 0x9c, 0x21,
|
|
0x10, 0x01, 0x02, 0x13, 0x2a, 0x10, 0x9b, 0x11, 0x00, 0x04, 0x24, 0x2b, 0x02, 0x9a, 0x01, 0x0a,
|
|
0x11, 0x29, 0x22, 0x2b, 0x03, 0x1b, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x11, 0x28, 0x11, 0x1c,
|
|
0x02, 0x2a, 0x03, 0xab, 0x10, 0x1a, 0x13, 0x9d, 0x20, 0x00, 0x02, 0x11, 0x2a, 0x02, 0x13, 0x22,
|
|
0x24, 0x8c, 0x08, 0xa8, 0x20, 0x10, 0x11, 0xa9, 0x10, 0x29, 0x20, 0x21, 0x11, 0x98, 0x11, 0x02,
|
|
0x1b, 0x21, 0x12, 0xab, 0x01, 0x21, 0xaa, 0x12, 0xaa, 0x10, 0x20, 0x21, 0x19, 0x12, 0x18, 0x11,
|
|
0xaa, 0x00, 0xa8, 0x01, 0x10, 0x21, 0x12, 0x89, 0x02, 0x2a, 0x11, 0x1b, 0x03, 0xab, 0x01, 0x10,
|
|
0x21, 0x03, 0xab, 0x01, 0x10, 0x21, 0x12, 0x0a, 0x12, 0x23, 0x8b, 0x11, 0xa8, 0x02, 0x0d, 0x04,
|
|
0x14, 0x2b, 0x22, 0xac, 0x14, 0x10, 0x01, 0x1a, 0x10, 0x20, 0xac, 0x9a, 0x14, 0x15, 0x8d, 0x20,
|
|
0xa9, 0x10, 0x20, 0x21, 0x11, 0x98, 0x01, 0x12, 0x0b, 0x11, 0x22, 0x9b, 0x00, 0x09, 0x02, 0x28,
|
|
0x12, 0x13, 0x2b, 0x22, 0xac, 0x00, 0x09, 0x02, 0x28, 0x12, 0x22, 0x13, 0x14, 0xac, 0x00, 0x10,
|
|
0x11, 0x09, 0x11, 0x02, 0x28, 0x12, 0x13, 0x2b, 0x22, 0xac, 0x18, 0x11, 0x12, 0x03, 0x14, 0xab,
|
|
0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b,
|
|
0x01, 0x98, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x01, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22,
|
|
0x2c, 0x03, 0x2b, 0x01, 0x10, 0x11, 0xa8, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x08, 0xa8,
|
|
0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x00, 0x20, 0x11, 0x88, 0x00, 0x0c, 0x02, 0x2a, 0x00,
|
|
0x19, 0x10, 0x1c, 0x10, 0x28, 0x14, 0xac, 0x23, 0x14, 0x03, 0x01, 0x10, 0x29, 0x14, 0x15, 0x8d,
|
|
0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x00, 0x99, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b,
|
|
0x11, 0xa8, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x01, 0x10, 0xa9, 0x02, 0x2a, 0x02, 0x04,
|
|
0x2c, 0x03, 0x1b, 0x08, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x00, 0x99, 0x02, 0x2a, 0x12,
|
|
0x1c, 0x04, 0x2c, 0x11, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x01, 0x10, 0xa9, 0x02, 0x2a,
|
|
0x12, 0x1c, 0x04, 0x2c, 0x28, 0x88, 0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x08, 0x02, 0x9a, 0x04,
|
|
0x02, 0x24, 0x2a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x00, 0x99, 0x02, 0x22,
|
|
0x24, 0x04, 0x0a, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x11, 0x28, 0x00, 0x99, 0x02, 0x22,
|
|
0x24, 0x04, 0x0a, 0x01, 0x10, 0x11, 0xa8, 0x01, 0x21, 0x24, 0x04, 0x09, 0x08, 0xa8, 0x01, 0x2b,
|
|
0x03, 0xa9, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x03, 0xa9, 0x01, 0x04, 0x24, 0x29, 0x11,
|
|
0xa8, 0x01, 0x04, 0x24, 0x29, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x01, 0x04,
|
|
0x24, 0x29, 0x08, 0xa8, 0x01, 0x02, 0x13, 0x1c, 0x13, 0x22, 0x29, 0x11, 0xa8, 0x00, 0x0c, 0x01,
|
|
0x11, 0x22, 0x13, 0x8b, 0x00, 0x0d, 0x00, 0x10, 0x21, 0x1a, 0x02, 0x22, 0x24, 0x8c, 0x02, 0x04,
|
|
0x24, 0x2a, 0x23, 0x12, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x11, 0xa8,
|
|
0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12,
|
|
0x0a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x09, 0xa9, 0x02, 0x04,
|
|
0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0x21, 0x89, 0x02, 0x1b, 0x02, 0x04, 0x2c, 0x12, 0x1c,
|
|
0x12, 0x2a, 0x13, 0x2b, 0x22, 0xab, 0x03, 0x04, 0x2c, 0x03, 0x12, 0x2a, 0x14, 0x15, 0x8d, 0x24,
|
|
0x04, 0x02, 0x22, 0x23, 0x1b, 0x00, 0x99, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x11, 0xa8, 0x24,
|
|
0x04, 0x02, 0x22, 0x23, 0x1b, 0x01, 0x10, 0xa9, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x09, 0xa9,
|
|
0x12, 0x1c, 0x00, 0x99, 0x12, 0x1c, 0x11, 0xa8, 0x12, 0x1c, 0x01, 0x10, 0xa9, 0x12, 0x1c, 0x09,
|
|
0xa9, 0x00, 0x2a, 0x11, 0x28, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0x23, 0x2c,
|
|
0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x22, 0x0a,
|
|
0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01,
|
|
0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x09, 0xa9, 0x19, 0x02, 0x2a, 0x9b, 0x02, 0x04,
|
|
0x24, 0x22, 0x0a, 0x04, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x00, 0x99, 0x02, 0x04, 0x14,
|
|
0x2b, 0x24, 0x2a, 0x11, 0xa8, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x02, 0x04,
|
|
0x14, 0x2b, 0x24, 0x2a, 0x09, 0xa9, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x11, 0xa8, 0x00, 0x0c,
|
|
0x02, 0x11, 0x22, 0x13, 0x8a, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x09, 0xa9, };
|
|
|
|
|
|
|
|
//Set this if you are only using CNFG to create an OpenGL context.
|
|
#ifndef CNFGCONTEXTONLY
|
|
uint32_t CNFGDialogColor;
|
|
|
|
void CNFGDrawBox( short x1, short y1, short x2, short y2 )
|
|
{
|
|
uint32_t lc = CNFGLastColor;
|
|
CNFGColor( CNFGDialogColor );
|
|
CNFGTackRectangle( x1, y1, x2, y2 );
|
|
CNFGColor( lc );
|
|
CNFGTackSegment( x1, y1, x2, y1 );
|
|
CNFGTackSegment( x2, y1, x2, y2 );
|
|
CNFGTackSegment( x2, y2, x1, y2 );
|
|
CNFGTackSegment( x1, y2, x1, y1 );
|
|
}
|
|
|
|
void CNFGDrawText( const char * text, short scale )
|
|
{
|
|
const unsigned char * lmap;
|
|
float iox = (float)CNFGPenX; //x offset
|
|
float ioy = (float)CNFGPenY; //y offset
|
|
|
|
int place = 0;
|
|
unsigned short index;
|
|
int bQuit = 0;
|
|
while( text[place] )
|
|
{
|
|
unsigned char c = text[place];
|
|
switch( c )
|
|
{
|
|
case 9: // tab
|
|
iox += 12 * scale;
|
|
break;
|
|
case 10: // linefeed
|
|
iox = (float)CNFGPenX;
|
|
ioy += 6 * scale;
|
|
break;
|
|
default:
|
|
index = RawdrawFontCharMap[c];
|
|
if( index == 65535 )
|
|
{
|
|
iox += 3 * scale;
|
|
break;
|
|
}
|
|
|
|
lmap = &RawdrawFontCharData[index];
|
|
short penx = 0, peny = 0;
|
|
unsigned char start_seg = 1;
|
|
do
|
|
{
|
|
unsigned char data = (*(lmap++));
|
|
short x1 = (short)(((data >> 4) & 0x07)*scale + iox);
|
|
short y1 = (short)((data & 0x07)*scale + ioy);
|
|
if( start_seg )
|
|
{
|
|
penx = x1;
|
|
peny = y1;
|
|
start_seg = 0;
|
|
if( data & 0x08 )
|
|
CNFGTackPixel( x1, y1 );
|
|
}
|
|
else
|
|
{
|
|
CNFGTackSegment( penx, peny, x1, y1 );
|
|
penx = x1;
|
|
peny = y1;
|
|
}
|
|
if( data & 0x08 ) start_seg = 1;
|
|
bQuit = data & 0x80;
|
|
} while( !bQuit );
|
|
|
|
iox += 3 * scale;
|
|
}
|
|
place++;
|
|
}
|
|
}
|
|
|
|
#ifndef FONT_CREATION_TOOL
|
|
#ifdef _CNFG_FANCYFONT
|
|
|
|
void CNFGDrawNiceText(const char* text, short scale)
|
|
{
|
|
const unsigned char* lmap;
|
|
float iox = (float)CNFGPenX; //x offset
|
|
float ioy = (float)CNFGPenY; //y offset
|
|
|
|
int place = 0;
|
|
unsigned short index;
|
|
int bQuit = 0;
|
|
int segmentEnd = 0;
|
|
while (text[place]) {
|
|
unsigned char c = text[place];
|
|
switch (c)
|
|
{
|
|
case 9: // tab
|
|
iox += 16 * scale;
|
|
break;
|
|
case 10: // linefeed
|
|
iox = (float)CNFGPenX;
|
|
ioy += 6 * scale;
|
|
break;
|
|
default:
|
|
index = CharIndex[c];
|
|
if (index == 0) {
|
|
iox += 4 * scale;
|
|
break;
|
|
}
|
|
|
|
lmap = &FontData[index];
|
|
|
|
short charWidth = ((*lmap) & 0xE0) >> 5; //0b11100000
|
|
short xbase = ((*lmap) & 0x18) >> 3; //0b00011000
|
|
short ybase = (*lmap) & 0x07; //0b00000111
|
|
lmap++;
|
|
do {
|
|
|
|
int x1 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale); //0b00111000
|
|
int y1 = (((*lmap) & 0x07) * scale + ioy + ybase * scale);
|
|
segmentEnd = *lmap & 0x40;
|
|
int x2 = 0;
|
|
int y2 = 0;
|
|
lmap++;
|
|
if (segmentEnd) {
|
|
x2 = x1;
|
|
y2 = y1;
|
|
}
|
|
else {
|
|
|
|
x2 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale);
|
|
y2 = (((*lmap) & 0x07) * scale + ioy + ybase * scale);
|
|
|
|
}
|
|
|
|
|
|
CNFGTackSegment(x1, y1, x2, y2);
|
|
bQuit = *(lmap - 1) & 0x80;
|
|
|
|
} while (!bQuit);
|
|
iox += (charWidth + 2) * scale;
|
|
//iox += 8 * scale;
|
|
}
|
|
place++;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize )
|
|
{
|
|
int charsx = 0;
|
|
int charsy = 1;
|
|
int charsline = 0;
|
|
const char * s;
|
|
|
|
for( s = text; *s; s++ )
|
|
{
|
|
if( *s == '\n' )
|
|
{
|
|
charsline = 0;
|
|
if( *(s+1) )
|
|
charsy++;
|
|
}
|
|
else
|
|
{
|
|
charsline++;
|
|
if( charsline > charsx )
|
|
charsx = charsline;
|
|
}
|
|
}
|
|
|
|
*w = charsx * textsize * 3-1*textsize;
|
|
*h = charsy * textsize * 6;
|
|
}
|
|
|
|
#if defined( CNFG_BATCH )
|
|
|
|
//This is the path by which we convert rawdraw functionality
|
|
//into nice batched triangle streams.
|
|
|
|
//Just FYI we use floats for geometry instead of shorts becase it is harder
|
|
//to triangularize a diagonal line int triangles with shorts and have it look good.
|
|
void CNFGEmitBackendTriangles( const float * fv, const uint32_t * col, int nr_verts );
|
|
|
|
//If on WASM, sqrtf is implied. On other platforms, need sqrtf from math.h
|
|
#ifdef __wasm__
|
|
float sqrtf( float f );
|
|
#define cnfg_sqrtf sqrtf
|
|
#elif defined( __TINYC__ ) && defined( WIN32 )
|
|
#define cnfg_sqrtf sqrt
|
|
#else
|
|
#define cnfg_sqrtf sqrtf
|
|
#include <math.h>
|
|
#endif
|
|
|
|
//Geometry batching system - so we can batch geometry and deliver it all at once.
|
|
float CNFGVertDataV[CNFG_BATCH*3];
|
|
uint32_t CNFGVertDataC[CNFG_BATCH];
|
|
int CNFGVertPlace;
|
|
static float wgl_last_width_over_2 = .5f;
|
|
|
|
static void EmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 )
|
|
{
|
|
//Because quads are really useful, but it's best to keep them all triangles if possible.
|
|
//This lets us draw arbitrary quads.
|
|
if( CNFGVertPlace >= CNFG_BATCH-6 ) CNFGFlushRender();
|
|
float * fv = &CNFGVertDataV[CNFGVertPlace*3];
|
|
fv[0] = cx0; fv[1] = cy0;
|
|
fv[3] = cx1; fv[4] = cy1;
|
|
fv[6] = cx2; fv[7] = cy2;
|
|
fv[9] = cx2; fv[10] = cy2;
|
|
fv[12] = cx1; fv[13] = cy1;
|
|
fv[15] = cx3; fv[16] = cy3;
|
|
uint32_t * col = &CNFGVertDataC[CNFGVertPlace];
|
|
uint32_t color = CNFGLastColor;
|
|
col[0] = color; col[1] = color; col[2] = color; col[3] = color; col[4] = color; col[5] = color;
|
|
CNFGVertPlace += 6;
|
|
}
|
|
|
|
|
|
#if !defined( CNFGRASTERIZER ) && !defined( CNFGHTTP )
|
|
|
|
void CNFGTackPixel( short x1, short y1 )
|
|
{
|
|
x1++; y1++;
|
|
const float l2 = wgl_last_width_over_2;
|
|
const float l2u = wgl_last_width_over_2+0.5f;
|
|
EmitQuad( x1-l2u, y1-l2u, x1+l2, y1-l2u, x1-l2u, y1+l2, x1+l2, y1+l2 );
|
|
}
|
|
|
|
|
|
void CNFGTackSegment( short x1, short y1, short x2, short y2 )
|
|
{
|
|
float ix1 = x1;
|
|
float iy1 = y1;
|
|
float ix2 = x2;
|
|
float iy2 = y2;
|
|
|
|
float dx = ix2-ix1;
|
|
float dy = iy2-iy1;
|
|
float imag = 1.f/(float)(cnfg_sqrtf(dx*dx+dy*dy));
|
|
dx *= imag;
|
|
dy *= imag;
|
|
float orthox = dy*wgl_last_width_over_2;
|
|
float orthoy =-dx*wgl_last_width_over_2;
|
|
|
|
ix2 += dx/2 + 0.5f;
|
|
iy2 += dy/2 + 0.5f;
|
|
ix1 -= dx/2 - 0.5f;
|
|
iy1 -= dy/2 - 0.5f;
|
|
|
|
//This logic is incorrect. XXX FIXME.
|
|
EmitQuad( (ix1 - orthox), (iy1 - orthoy), (ix1 + orthox), (iy1 + orthoy), (ix2 - orthox), (iy2 - orthoy), ( ix2 + orthox), ( iy2 + orthoy) );
|
|
}
|
|
|
|
void CNFGTackRectangle( short x1, short y1, short x2, short y2 )
|
|
{
|
|
float ix1 = x1;
|
|
float iy1 = y1;
|
|
float ix2 = x2;
|
|
float iy2 = y2;
|
|
EmitQuad( ix1,iy1,ix2,iy1,ix1,iy2,ix2,iy2 );
|
|
}
|
|
|
|
void CNFGTackPoly( RDPoint * points, int verts )
|
|
{
|
|
int i;
|
|
int tris = verts-2;
|
|
if( CNFGVertPlace >= CNFG_BATCH-tris*3 ) CNFGFlushRender();
|
|
|
|
uint32_t color = CNFGLastColor;
|
|
short * ptrsrc = (short*)points;
|
|
|
|
for( i = 0; i < tris; i++ )
|
|
{
|
|
float * fv = &CNFGVertDataV[CNFGVertPlace*3];
|
|
fv[0] = ptrsrc[0];
|
|
fv[1] = ptrsrc[1];
|
|
fv[3] = ptrsrc[i*2+2];
|
|
fv[4] = ptrsrc[i*2+3];
|
|
fv[6] = ptrsrc[i*2+4];
|
|
fv[7] = ptrsrc[i*2+5];
|
|
|
|
uint32_t * col = &CNFGVertDataC[CNFGVertPlace];
|
|
col[0] = color;
|
|
col[1] = color;
|
|
col[2] = color;
|
|
|
|
CNFGVertPlace += 3;
|
|
}
|
|
}
|
|
|
|
uint32_t CNFGColor( uint32_t RGB )
|
|
{
|
|
return CNFGLastColor = RGB;
|
|
}
|
|
|
|
void CNFGSetLineWidth( short width )
|
|
{
|
|
wgl_last_width_over_2 = width/2.0f;// + 0.5;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#if !defined( __wasm__ ) && !defined( CNFGHTTP )
|
|
//In WASM, Javascript takes over this functionality.
|
|
|
|
//Shader compilation errors go to stderr.
|
|
#include <stdio.h>
|
|
|
|
#ifndef GL_VERTEX_SHADER
|
|
#define GL_FRAGMENT_SHADER 0x8B30
|
|
#define GL_VERTEX_SHADER 0x8B31
|
|
#define GL_COMPILE_STATUS 0x8B81
|
|
#define GL_INFO_LOG_LENGTH 0x8B84
|
|
#define GL_LINK_STATUS 0x8B82
|
|
#define GL_TEXTURE_2D 0x0DE1
|
|
#define GL_CLAMP_TO_EDGE 0x812F
|
|
#define LGLchar char
|
|
#else
|
|
#define LGLchar GLchar
|
|
#endif
|
|
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
#define CNFGOGL_NEED_EXTENSION
|
|
#include <GL/gl.h>
|
|
#endif
|
|
|
|
#ifdef CNFGOGL_NEED_EXTENSION
|
|
// If we are going to be defining our own function pointer call
|
|
#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
|
|
// Make sure to use __stdcall on Windows
|
|
#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \
|
|
typedef ret (__stdcall *CNFGTYPE##name)( __VA_ARGS__ ); \
|
|
ret (__stdcall *CNFG##name)( __VA_ARGS__ );
|
|
#else
|
|
#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \
|
|
typedef ret (*CNFGTYPE##name)( __VA_ARGS__ ); \
|
|
ret (*CNFG##name)( __VA_ARGS__ );
|
|
#endif
|
|
#else
|
|
//If we are going to be defining the real call
|
|
#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \
|
|
ret name (__VA_ARGS__);
|
|
#endif
|
|
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
int (*MyFunc)( int program, const LGLchar *name );
|
|
|
|
CHEWTYPEDEF( GLint, glGetUniformLocation, return, (program,name), GLuint program, const LGLchar *name )
|
|
CHEWTYPEDEF( void, glEnableVertexAttribArray, , (index), GLuint index )
|
|
CHEWTYPEDEF( void, glUseProgram, , (program), GLuint program )
|
|
CHEWTYPEDEF( void, glGetProgramInfoLog, , (program,maxLength, length, infoLog), GLuint program, GLsizei maxLength, GLsizei *length, LGLchar *infoLog )
|
|
CHEWTYPEDEF( void, glGetProgramiv, , (program,pname,params), GLuint program, GLenum pname, GLint *params )
|
|
CHEWTYPEDEF( void, glBindAttribLocation, , (program,index,name), GLuint program, GLuint index, const LGLchar *name )
|
|
CHEWTYPEDEF( void, glGetShaderiv, , (shader,pname,params), GLuint shader, GLenum pname, GLint *params )
|
|
CHEWTYPEDEF( GLuint, glCreateShader, return, (e), GLenum e )
|
|
CHEWTYPEDEF( void, glVertexAttribPointer, , (index,size,type,normalized,stride,pointer), GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer )
|
|
CHEWTYPEDEF( void, glShaderSource, , (shader,count,string,length), GLuint shader, GLsizei count, const LGLchar *const*string, const GLint *length )
|
|
CHEWTYPEDEF( void, glAttachShader, , (program,shader), GLuint program, GLuint shader )
|
|
CHEWTYPEDEF( void, glCompileShader, ,(shader), GLuint shader )
|
|
CHEWTYPEDEF( void, glGetShaderInfoLog , , (shader,maxLength, length, infoLog), GLuint shader, GLsizei maxLength, GLsizei *length, LGLchar *infoLog )
|
|
CHEWTYPEDEF( GLuint, glCreateProgram, return, () , void )
|
|
CHEWTYPEDEF( void, glLinkProgram, , (program), GLuint program )
|
|
CHEWTYPEDEF( void, glDeleteShader, , (shader), GLuint shader )
|
|
CHEWTYPEDEF( void, glUniform4f, , (location,v0,v1,v2,v3), GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 )
|
|
CHEWTYPEDEF( void, glUniform1i, , (location,i0), GLint location, GLint i0 )
|
|
CHEWTYPEDEF( void, glActiveTexture, , (texture), GLenum texture )
|
|
|
|
#ifndef CNFGOGL_NEED_EXTENSION
|
|
#define CNFGglGetUniformLocation glGetUniformLocation
|
|
#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray
|
|
#define CNFGglUseProgram glUseProgram
|
|
#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray
|
|
#define CNFGglUseProgram glUseProgram
|
|
#define CNFGglGetProgramInfoLog glGetProgramInfoLog
|
|
#define CNFGglGetProgramiv glGetProgramiv
|
|
#define CNFGglShaderSource glShaderSource
|
|
#define CNFGglCreateShader glCreateShader
|
|
#define CNFGglAttachShader glAttachShader
|
|
#define CNFGglGetShaderiv glGetShaderiv
|
|
#define CNFGglCompileShader glCompileShader
|
|
#define CNFGglGetShaderInfoLog glGetShaderInfoLog
|
|
#define CNFGglCreateProgram glCreateProgram
|
|
#define CNFGglLinkProgram glLinkProgram
|
|
#define CNFGglDeleteShader glDeleteShader
|
|
#define CNFGglUniform4f glUniform4f
|
|
#define CNFGglBindAttribLocation glBindAttribLocation
|
|
#define CNFGglVertexAttribPointer glVertexAttribPointer
|
|
#define CNFGglUniform1i glUniform1i
|
|
#define CNFGglActiveTexture glActiveTexture
|
|
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
};
|
|
#endif
|
|
|
|
#ifdef CNFGOGL_NEED_EXTENSION
|
|
#if defined( WIN32 ) || defined( WINDOWS ) || defined( WIN64 )
|
|
|
|
//From https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions
|
|
void * CNFGGetProcAddress(const char *name)
|
|
{
|
|
void *p = (void *)wglGetProcAddress(name);
|
|
if(p == 0 ||
|
|
(p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
|
|
(p == (void*)-1) )
|
|
{
|
|
static HMODULE module;
|
|
if( !module ) module = LoadLibraryA("opengl32.dll");
|
|
p = (void *)GetProcAddress(module, name);
|
|
}
|
|
// We were unable to load the required openGL function
|
|
if (!p) {
|
|
fprintf(stderr,"[rawdraw][warn]: Unable to load openGL extension \"%s\"\n", name);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
#else
|
|
#include <dlfcn.h>
|
|
|
|
|
|
void * CNFGGetProcAddress(const char *name)
|
|
{
|
|
//Tricky use RTLD_NEXT first so we don't accidentally link against ourselves.
|
|
void * v1 = dlsym( (void*)((intptr_t)-1) /*RTLD_NEXT = -1*/ /*RTLD_DEFAULT = 0*/, name );
|
|
//printf( "%s = %p\n", name, v1 );
|
|
if( !v1 ) v1 = dlsym( 0, name );
|
|
return v1;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Try and load openGL extension functions required for rawdraw
|
|
static void CNFGLoadExtensionsInternal()
|
|
{
|
|
CNFGglGetUniformLocation = (CNFGTYPEglGetUniformLocation) CNFGGetProcAddress( "glGetUniformLocation" );
|
|
CNFGglEnableVertexAttribArray = (CNFGTYPEglEnableVertexAttribArray)CNFGGetProcAddress( "glEnableVertexAttribArray" );
|
|
CNFGglUseProgram = (CNFGTYPEglUseProgram)CNFGGetProcAddress( "glUseProgram" );
|
|
CNFGglGetProgramInfoLog = (CNFGTYPEglGetProgramInfoLog)CNFGGetProcAddress( "glGetProgramInfoLog" );
|
|
CNFGglBindAttribLocation = (CNFGTYPEglBindAttribLocation)CNFGGetProcAddress( "glBindAttribLocation" );
|
|
CNFGglGetProgramiv = (CNFGTYPEglGetProgramiv)CNFGGetProcAddress( "glGetProgramiv" );
|
|
CNFGglGetShaderiv = (CNFGTYPEglGetShaderiv)CNFGGetProcAddress( "glGetShaderiv" );
|
|
CNFGglVertexAttribPointer = (CNFGTYPEglVertexAttribPointer)CNFGGetProcAddress( "glVertexAttribPointer" );
|
|
CNFGglCreateShader = (CNFGTYPEglCreateShader)CNFGGetProcAddress( "glCreateShader" );
|
|
CNFGglShaderSource = (CNFGTYPEglShaderSource)CNFGGetProcAddress( "glShaderSource" );
|
|
CNFGglAttachShader = (CNFGTYPEglAttachShader)CNFGGetProcAddress( "glAttachShader" );
|
|
CNFGglCompileShader = (CNFGTYPEglCompileShader)CNFGGetProcAddress( "glCompileShader" );
|
|
CNFGglGetShaderInfoLog = (CNFGTYPEglGetShaderInfoLog)CNFGGetProcAddress( "glGetShaderInfoLog" );
|
|
CNFGglDeleteShader = (CNFGTYPEglDeleteShader)CNFGGetProcAddress( "glDeleteShader" );
|
|
CNFGglLinkProgram = (CNFGTYPEglLinkProgram)CNFGGetProcAddress( "glLinkProgram" );
|
|
CNFGglCreateProgram = (CNFGTYPEglCreateProgram)CNFGGetProcAddress( "glCreateProgram" );
|
|
CNFGglUniform4f = (CNFGTYPEglUniform4f)CNFGGetProcAddress( "glUniform4f" );
|
|
CNFGglUniform1i = (CNFGTYPEglUniform1i)CNFGGetProcAddress( "glUniform1i" );
|
|
CNFGglActiveTexture = (CNFGTYPEglActiveTexture)CNFGGetProcAddress("glActiveTexture");
|
|
|
|
// Check if any of these functions didn't get loaded
|
|
uint8_t not_all_functions_loaded =
|
|
!CNFGglGetUniformLocation || !CNFGglEnableVertexAttribArray || !CNFGglUseProgram ||
|
|
!CNFGglGetProgramInfoLog || !CNFGglBindAttribLocation || !CNFGglGetProgramiv ||
|
|
!CNFGglVertexAttribPointer || !CNFGglCreateShader || !CNFGglShaderSource ||
|
|
!CNFGglAttachShader || !CNFGglCompileShader || !CNFGglGetShaderInfoLog ||
|
|
!CNFGglDeleteShader || !CNFGglLinkProgram || !CNFGglCreateProgram ||
|
|
!CNFGglUniform4f || !CNFGglUniform1i || !CNFGglActiveTexture;
|
|
if (not_all_functions_loaded) {
|
|
fprintf(
|
|
stderr,
|
|
"[rawdraw][err]: Unable to load all openGL extensions required for rawdraw\n"
|
|
"\tPlease update your graphics drivers or unexpected crashes may occur.\n"
|
|
);
|
|
}
|
|
|
|
// Give a very stern warning if unable to create or compile shaders
|
|
if (!CNFGglCreateShader || !CNFGglCompileShader) {
|
|
fprintf(
|
|
stderr,
|
|
"[rawdraw][err]: Unable to create or compile shaders, this will cause a fatal error if "
|
|
"openGL is used.\n"
|
|
"\tUpdate your video graphics drivers or switch to software graphics.\n"
|
|
);
|
|
}
|
|
}
|
|
#else
|
|
static void CNFGLoadExtensionsInternal() { }
|
|
#endif
|
|
|
|
|
|
|
|
GLuint gRDShaderProg = -1;
|
|
GLuint gRDBlitProg = -1;
|
|
GLuint gRDShaderProgUX = -1;
|
|
GLuint gRDBlitProgUX = -1;
|
|
GLuint gRDBlitProgUT = -1;
|
|
GLuint gRDBlitProgTex = -1;
|
|
GLuint gRDLastResizeW;
|
|
GLuint gRDLastResizeH;
|
|
|
|
|
|
GLuint CNFGGLInternalLoadShader( const char * vertex_shader, const char * fragment_shader )
|
|
{
|
|
GLuint fragment_shader_object = 0;
|
|
GLuint vertex_shader_object = 0;
|
|
GLuint program = 0;
|
|
int ret;
|
|
|
|
vertex_shader_object = CNFGglCreateShader(GL_VERTEX_SHADER);
|
|
if (!vertex_shader_object) {
|
|
fprintf( stderr, "Error: glCreateShader(GL_VERTEX_SHADER) "
|
|
"failed: 0x%08X\n", glGetError());
|
|
goto fail;
|
|
}
|
|
|
|
CNFGglShaderSource(vertex_shader_object, 1, &vertex_shader, NULL);
|
|
CNFGglCompileShader(vertex_shader_object);
|
|
|
|
CNFGglGetShaderiv(vertex_shader_object, GL_COMPILE_STATUS, &ret);
|
|
if (!ret) {
|
|
fprintf( stderr,"Error: vertex shader compilation failed!\n");
|
|
CNFGglGetShaderiv(vertex_shader_object, GL_INFO_LOG_LENGTH, &ret);
|
|
|
|
if (ret > 1) {
|
|
//TODO: Refactor to remove malloc reliance.
|
|
#ifndef __clang__
|
|
char * log = (char*)alloca(ret);
|
|
CNFGglGetShaderInfoLog(vertex_shader_object, ret, NULL, log);
|
|
fprintf( stderr, "%s", log);
|
|
#endif
|
|
}
|
|
goto fail;
|
|
}
|
|
|
|
fragment_shader_object = CNFGglCreateShader(GL_FRAGMENT_SHADER);
|
|
if (!fragment_shader_object) {
|
|
fprintf( stderr, "Error: glCreateShader(GL_FRAGMENT_SHADER) "
|
|
"failed: 0x%08X\n", glGetError());
|
|
goto fail;
|
|
}
|
|
|
|
CNFGglShaderSource(fragment_shader_object, 1, &fragment_shader, NULL);
|
|
CNFGglCompileShader(fragment_shader_object);
|
|
|
|
CNFGglGetShaderiv(fragment_shader_object, GL_COMPILE_STATUS, &ret);
|
|
if (!ret) {
|
|
fprintf( stderr, "Error: fragment shader compilation failed!\n");
|
|
CNFGglGetShaderiv(fragment_shader_object, GL_INFO_LOG_LENGTH, &ret);
|
|
|
|
if (ret > 1) {
|
|
//TODO: Refactor to remove malloc reliance.
|
|
#ifndef __clang__
|
|
char * log = (char*)malloc(ret);
|
|
CNFGglGetShaderInfoLog(fragment_shader_object, ret, NULL, log);
|
|
fprintf( stderr, "%s", log);
|
|
free( log );
|
|
#endif
|
|
}
|
|
goto fail;
|
|
}
|
|
|
|
program = CNFGglCreateProgram();
|
|
if (!program) {
|
|
fprintf( stderr, "Error: failed to create program!\n");
|
|
goto fail;
|
|
}
|
|
|
|
CNFGglAttachShader(program, vertex_shader_object);
|
|
CNFGglAttachShader(program, fragment_shader_object);
|
|
|
|
CNFGglBindAttribLocation(program, 0, "a0");
|
|
CNFGglBindAttribLocation(program, 1, "a1");
|
|
|
|
CNFGglLinkProgram(program);
|
|
|
|
CNFGglGetProgramiv(program, GL_LINK_STATUS, &ret);
|
|
if (!ret) {
|
|
fprintf( stderr, "Error: program linking failed!\n");
|
|
CNFGglGetProgramiv(program, GL_INFO_LOG_LENGTH, &ret);
|
|
|
|
if (ret > 1) {
|
|
//TODO: Refactor to remove malloc reliance.
|
|
#ifndef __clang__
|
|
char *log = (char*)alloca(ret);
|
|
CNFGglGetProgramInfoLog(program, ret, NULL, log);
|
|
fprintf( stderr, "%s", log);
|
|
#endif
|
|
}
|
|
goto fail;
|
|
}
|
|
return program;
|
|
fail:
|
|
if( !vertex_shader_object ) CNFGglDeleteShader( vertex_shader_object );
|
|
if( !fragment_shader_object ) CNFGglDeleteShader( fragment_shader_object );
|
|
if( !program ) CNFGglDeleteShader( program );
|
|
return -1;
|
|
}
|
|
|
|
#if defined( CNFGEWGL ) && !defined( CNFG_NO_PRECISION )
|
|
#define PRECISIONA "lowp"
|
|
#define PRECISIONB "mediump"
|
|
#else
|
|
#define PRECISIONA
|
|
#define PRECISIONB
|
|
#endif
|
|
|
|
void CNFGSetupBatchInternal()
|
|
{
|
|
short w, h;
|
|
|
|
CNFGLoadExtensionsInternal();
|
|
|
|
CNFGGetDimensions( &w, &h );
|
|
|
|
gRDShaderProg = CNFGGLInternalLoadShader(
|
|
"uniform vec4 xfrm;"
|
|
"attribute vec3 a0;"
|
|
"attribute vec4 a1;"
|
|
"varying " PRECISIONA " vec4 vc;"
|
|
"void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); vc = a1; }",
|
|
|
|
"varying " PRECISIONA " vec4 vc;"
|
|
"void main() { gl_FragColor = vec4(vc.abgr); }"
|
|
);
|
|
|
|
CNFGglUseProgram( gRDShaderProg );
|
|
gRDShaderProgUX = CNFGglGetUniformLocation ( gRDShaderProg , "xfrm" );
|
|
|
|
|
|
gRDBlitProg = CNFGGLInternalLoadShader(
|
|
"uniform vec4 xfrm;"
|
|
"attribute vec3 a0;"
|
|
"attribute vec4 a1;"
|
|
"varying " PRECISIONB " vec2 tc;"
|
|
"void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); tc = a1.xy; }",
|
|
|
|
"varying " PRECISIONB " vec2 tc;"
|
|
"uniform sampler2D tex;"
|
|
"void main() { gl_FragColor = texture2D(tex,tc)."
|
|
|
|
#if !defined( CNFGRASTERIZER )
|
|
"wzyx"
|
|
#else
|
|
"wxyz"
|
|
#endif
|
|
";}" );
|
|
|
|
CNFGglUseProgram( gRDBlitProg );
|
|
gRDBlitProgUX = CNFGglGetUniformLocation ( gRDBlitProg , "xfrm" );
|
|
gRDBlitProgUT = CNFGglGetUniformLocation ( gRDBlitProg , "tex" );
|
|
glGenTextures( 1, &gRDBlitProgTex );
|
|
|
|
CNFGglEnableVertexAttribArray(0);
|
|
CNFGglEnableVertexAttribArray(1);
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask( GL_FALSE );
|
|
glEnable( GL_BLEND );
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
CNFGVertPlace = 0;
|
|
}
|
|
|
|
#ifndef CNFGRASTERIZER
|
|
void CNFGInternalResize(short x, short y)
|
|
#else
|
|
void CNFGInternalResizeOGLBACKEND(short x, short y)
|
|
#endif
|
|
{
|
|
glViewport( 0, 0, x, y );
|
|
gRDLastResizeW = x;
|
|
gRDLastResizeH = y;
|
|
if (gRDShaderProg == 0xFFFFFFFF) { return; } // Prevent trying to set uniform if the shader isn't ready yet.
|
|
CNFGglUseProgram( gRDShaderProg );
|
|
CNFGglUniform4f( gRDShaderProgUX, 1.f/x, -1.f/y, -0.5f, 0.5f);
|
|
}
|
|
|
|
void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices )
|
|
{
|
|
CNFGglUseProgram( gRDShaderProg );
|
|
CNFGglUniform4f( gRDShaderProgUX, 1.f/gRDLastResizeW, -1.f/gRDLastResizeH, -0.5f, 0.5f);
|
|
CNFGglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
|
|
CNFGglVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
|
|
glDrawArrays( GL_TRIANGLES, 0, num_vertices);
|
|
}
|
|
|
|
|
|
#ifdef CNFGOGL
|
|
// this is here, so people don't have to include opengl
|
|
void CNFGDeleteTex( unsigned int tex )
|
|
{
|
|
glDeleteTextures(1, &tex);
|
|
}
|
|
|
|
unsigned int CNFGTexImage( uint32_t *data, int w, int h )
|
|
{
|
|
GLuint tex;
|
|
|
|
glGenTextures(1, &tex);
|
|
glEnable( GL_TEXTURE_2D );
|
|
CNFGglActiveTexture( 0 );
|
|
glBindTexture( GL_TEXTURE_2D, tex );
|
|
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
|
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, data );
|
|
|
|
return (unsigned int)tex;
|
|
}
|
|
|
|
void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h )
|
|
{
|
|
if( w == 0 || h == 0 ) return;
|
|
|
|
CNFGFlushRender();
|
|
|
|
CNFGglUseProgram( gRDBlitProg );
|
|
CNFGglUniform4f( gRDBlitProgUX,
|
|
1.f/gRDLastResizeW, -1.f/gRDLastResizeH,
|
|
-0.5f+x/(float)gRDLastResizeW, 0.5f-y/(float)gRDLastResizeH );
|
|
CNFGglUniform1i( gRDBlitProgUT, 0 );
|
|
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
|
|
const float verts[] = {
|
|
0,0, (float)w,0, (float)w,(float)h,
|
|
0,0, (float)w,(float)h, 0,(float)h, };
|
|
static const uint8_t tex_verts[] = {
|
|
0,0, 255,0, 255,255,
|
|
0,0, 255,255, 0,255 };
|
|
|
|
CNFGglVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
|
CNFGglVertexAttribPointer(1, 2, GL_UNSIGNED_BYTE, GL_TRUE, 0, tex_verts);
|
|
|
|
glDrawArrays( GL_TRIANGLES, 0, 6);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CNFGRASTERIZER
|
|
void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h )
|
|
#else
|
|
void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h )
|
|
#endif
|
|
{
|
|
glEnable( GL_TEXTURE_2D );
|
|
CNFGglActiveTexture( 0 );
|
|
glBindTexture( GL_TEXTURE_2D, gRDBlitProgTex );
|
|
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
|
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, data );
|
|
|
|
CNFGBlitTex( gRDBlitProgTex, x, y, w, h );
|
|
}
|
|
|
|
void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h )
|
|
{
|
|
#ifdef CNFGRASTERIZER
|
|
CNFGBlitImageInternal( data, 0, 0, w, h );
|
|
void CNFGSwapBuffersInternal();
|
|
CNFGSwapBuffersInternal();
|
|
#else
|
|
CNFGBlitImage( data, 0, 0, w, h );
|
|
#endif
|
|
}
|
|
|
|
#ifndef CNFGRASTERIZER
|
|
|
|
void CNFGFlushRender()
|
|
{
|
|
if( !CNFGVertPlace ) return;
|
|
CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace );
|
|
CNFGVertPlace = 0;
|
|
}
|
|
|
|
void CNFGClearFrame()
|
|
{
|
|
glClearColor( ((CNFGBGColor&0xff000000)>>24)/255.0f,
|
|
((CNFGBGColor&0xff0000)>>16)/255.0f,
|
|
(CNFGBGColor&0xff00)/65280.0f,
|
|
(CNFGBGColor&0xff)/255.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif //__wasm__
|
|
|
|
#else
|
|
|
|
void CNFGFlushRender() { }
|
|
|
|
#endif
|
|
|
|
|
|
#endif
|
|
#endif // CNFGHTTPSERVERONLY
|
|
#endif //_CNFG_C
|
|
|
|
|
|
#ifdef CNFG3D
|
|
//Copyright 2012-2017 <>< Charles Lohr
|
|
//You may license this file under the MIT/x11, NewBSD, or any GPL license.
|
|
//This is a series of tools useful for software rendering.
|
|
//Use of this file with OpenGL is untested.
|
|
|
|
#ifdef CNFG3D
|
|
|
|
|
|
#ifdef __wasm__
|
|
double sin( double v );
|
|
double cos( double v );
|
|
double tan( double v );
|
|
double sqrt( double v );
|
|
float sinf( float v );
|
|
float cosf( float v );
|
|
float tanf( float v );
|
|
float sqrtf( float v );
|
|
void tdMATCOPY( float * x, const float * y )
|
|
{
|
|
int i;
|
|
for( i = 0; i < 16; i++ ) x[i] = y[i];
|
|
}
|
|
#else
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#ifdef CNFG3D_USE_OGL_MAJOR
|
|
#define m00 0
|
|
#define m10 1
|
|
#define m20 2
|
|
#define m30 3
|
|
#define m01 4
|
|
#define m11 5
|
|
#define m21 6
|
|
#define m31 7
|
|
#define m02 8
|
|
#define m12 9
|
|
#define m22 10
|
|
#define m32 11
|
|
#define m03 12
|
|
#define m13 13
|
|
#define m23 14
|
|
#define m33 15
|
|
#else
|
|
#define m00 0
|
|
#define m01 1
|
|
#define m02 2
|
|
#define m03 3
|
|
#define m10 4
|
|
#define m11 5
|
|
#define m12 6
|
|
#define m13 7
|
|
#define m20 8
|
|
#define m21 9
|
|
#define m22 10
|
|
#define m23 11
|
|
#define m30 12
|
|
#define m31 13
|
|
#define m32 14
|
|
#define m33 15
|
|
#endif
|
|
|
|
void tdIdentity( float * f )
|
|
{
|
|
f[m00] = 1; f[m01] = 0; f[m02] = 0; f[m03] = 0;
|
|
f[m10] = 0; f[m11] = 1; f[m12] = 0; f[m13] = 0;
|
|
f[m20] = 0; f[m21] = 0; f[m22] = 1; f[m23] = 0;
|
|
f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 1;
|
|
}
|
|
|
|
void tdZero( float * f )
|
|
{
|
|
f[m00] = 0; f[m01] = 0; f[m02] = 0; f[m03] = 0;
|
|
f[m10] = 0; f[m11] = 0; f[m12] = 0; f[m13] = 0;
|
|
f[m20] = 0; f[m21] = 0; f[m22] = 0; f[m23] = 0;
|
|
f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 0;
|
|
}
|
|
|
|
void tdTranslate( float * f, float x, float y, float z )
|
|
{
|
|
float ftmp[16];
|
|
tdIdentity(ftmp);
|
|
ftmp[m03] += x;
|
|
ftmp[m13] += y;
|
|
ftmp[m23] += z;
|
|
tdMultiply( f, ftmp, f );
|
|
}
|
|
|
|
void tdScale( float * f, float x, float y, float z )
|
|
{
|
|
#if 0
|
|
f[m00] *= x;
|
|
f[m01] *= x;
|
|
f[m02] *= x;
|
|
f[m03] *= x;
|
|
|
|
f[m10] *= y;
|
|
f[m11] *= y;
|
|
f[m12] *= y;
|
|
f[m13] *= y;
|
|
|
|
f[m20] *= z;
|
|
f[m21] *= z;
|
|
f[m22] *= z;
|
|
f[m23] *= z;
|
|
#endif
|
|
|
|
float ftmp[16];
|
|
tdIdentity(ftmp);
|
|
ftmp[m00] *= x;
|
|
ftmp[m11] *= y;
|
|
ftmp[m22] *= z;
|
|
|
|
tdMultiply( f, ftmp, f );
|
|
|
|
}
|
|
|
|
void tdRotateAA( float * f, float angle, float ix, float iy, float iz )
|
|
{
|
|
float ftmp[16];
|
|
|
|
float c = tdCOS( angle*tdDEGRAD );
|
|
float s = tdSIN( angle*tdDEGRAD );
|
|
float absin = tdSQRT( ix*ix + iy*iy + iz*iz );
|
|
float x = ix/absin;
|
|
float y = iy/absin;
|
|
float z = iz/absin;
|
|
|
|
ftmp[m00] = x*x*(1-c)+c;
|
|
ftmp[m01] = x*y*(1-c)-z*s;
|
|
ftmp[m02] = x*z*(1-c)+y*s;
|
|
ftmp[m03] = 0;
|
|
|
|
ftmp[m10] = y*x*(1-c)+z*s;
|
|
ftmp[m11] = y*y*(1-c)+c;
|
|
ftmp[m12] = y*z*(1-c)-x*s;
|
|
ftmp[m13] = 0;
|
|
|
|
ftmp[m20] = x*z*(1-c)-y*s;
|
|
ftmp[m21] = y*z*(1-c)+x*s;
|
|
ftmp[m22] = z*z*(1-c)+c;
|
|
ftmp[m23] = 0;
|
|
|
|
ftmp[m30] = 0;
|
|
ftmp[m31] = 0;
|
|
ftmp[m32] = 0;
|
|
ftmp[m33] = 1;
|
|
|
|
tdMultiply( f, ftmp, f );
|
|
}
|
|
|
|
void tdRotateQuat( float * f, float qw, float qx, float qy, float qz )
|
|
{
|
|
float ftmp[16];
|
|
//float qw2 = qw*qw;
|
|
float qx2 = qx*qx;
|
|
float qy2 = qy*qy;
|
|
float qz2 = qz*qz;
|
|
|
|
ftmp[m00] = 1 - 2*qy2 - 2*qz2;
|
|
ftmp[m01] = 2*qx*qy - 2*qz*qw;
|
|
ftmp[m02] = 2*qx*qz + 2*qy*qw;
|
|
ftmp[m03] = 0;
|
|
|
|
ftmp[m10] = 2*qx*qy + 2*qz*qw;
|
|
ftmp[m11] = 1 - 2*qx2 - 2*qz2;
|
|
ftmp[m12] = 2*qy*qz - 2*qx*qw;
|
|
ftmp[m13] = 0;
|
|
|
|
ftmp[m20] = 2*qx*qz - 2*qy*qw;
|
|
ftmp[m21] = 2*qy*qz + 2*qx*qw;
|
|
ftmp[m22] = 1 - 2*qx2 - 2*qy2;
|
|
ftmp[m23] = 0;
|
|
|
|
ftmp[m30] = 0;
|
|
ftmp[m31] = 0;
|
|
ftmp[m32] = 0;
|
|
ftmp[m33] = 1;
|
|
|
|
tdMultiply( f, ftmp, f );
|
|
|
|
}
|
|
|
|
void tdRotateEA( float * f, float x, float y, float z )
|
|
{
|
|
float ftmp[16];
|
|
|
|
//x,y,z must be negated for some reason
|
|
float X = -x*2*tdQ_PI/360; //Reduced calulation for speed
|
|
float Y = -y*2*tdQ_PI/360;
|
|
float Z = -z*2*tdQ_PI/360;
|
|
float cx = tdCOS(X);
|
|
float sx = tdSIN(X);
|
|
float cy = tdCOS(Y);
|
|
float sy = tdSIN(Y);
|
|
float cz = tdCOS(Z);
|
|
float sz = tdSIN(Z);
|
|
|
|
//Row major (unless CNFG3D_USE_OGL_MAJOR is selected)
|
|
//manually transposed
|
|
ftmp[m00] = cy*cz;
|
|
ftmp[m10] = (sx*sy*cz)-(cx*sz);
|
|
ftmp[m20] = (cx*sy*cz)+(sx*sz);
|
|
ftmp[m30] = 0;
|
|
|
|
ftmp[m01] = cy*sz;
|
|
ftmp[m11] = (sx*sy*sz)+(cx*cz);
|
|
ftmp[m21] = (cx*sy*sz)-(sx*cz);
|
|
ftmp[m31] = 0;
|
|
|
|
ftmp[m02] = -sy;
|
|
ftmp[m12] = sx*cy;
|
|
ftmp[m22] = cx*cy;
|
|
ftmp[m32] = 0;
|
|
|
|
ftmp[m03] = 0;
|
|
ftmp[m13] = 0;
|
|
ftmp[m23] = 0;
|
|
ftmp[m33] = 1;
|
|
|
|
tdMultiply( f, ftmp, f );
|
|
}
|
|
|
|
void tdMultiply( float * fin1, float * fin2, float * fout )
|
|
{
|
|
float fotmp[16];
|
|
int i, k;
|
|
#ifdef CNFG3D_USE_OGL_MAJOR
|
|
fotmp[m00] = fin1[m00] * fin2[m00] + fin1[m01] * fin2[m10] + fin1[m02] * fin2[m20] + fin1[m03] * fin2[m30];
|
|
fotmp[m01] = fin1[m00] * fin2[m01] + fin1[m01] * fin2[m11] + fin1[m02] * fin2[m21] + fin1[m03] * fin2[m31];
|
|
fotmp[m02] = fin1[m00] * fin2[m02] + fin1[m01] * fin2[m12] + fin1[m02] * fin2[m22] + fin1[m03] * fin2[m32];
|
|
fotmp[m03] = fin1[m00] * fin2[m03] + fin1[m01] * fin2[m13] + fin1[m02] * fin2[m23] + fin1[m03] * fin2[m33];
|
|
|
|
fotmp[m10] = fin1[m10] * fin2[m00] + fin1[m11] * fin2[m10] + fin1[m12] * fin2[m20] + fin1[m13] * fin2[m30];
|
|
fotmp[m11] = fin1[m10] * fin2[m01] + fin1[m11] * fin2[m11] + fin1[m12] * fin2[m21] + fin1[m13] * fin2[m31];
|
|
fotmp[m12] = fin1[m10] * fin2[m02] + fin1[m11] * fin2[m12] + fin1[m12] * fin2[m22] + fin1[m13] * fin2[m32];
|
|
fotmp[m13] = fin1[m10] * fin2[m03] + fin1[m11] * fin2[m13] + fin1[m12] * fin2[m23] + fin1[m13] * fin2[m33];
|
|
|
|
fotmp[m20] = fin1[m20] * fin2[m00] + fin1[m21] * fin2[m10] + fin1[m22] * fin2[m20] + fin1[m23] * fin2[m30];
|
|
fotmp[m21] = fin1[m20] * fin2[m01] + fin1[m21] * fin2[m11] + fin1[m22] * fin2[m21] + fin1[m23] * fin2[m31];
|
|
fotmp[m22] = fin1[m20] * fin2[m02] + fin1[m21] * fin2[m12] + fin1[m22] * fin2[m22] + fin1[m23] * fin2[m32];
|
|
fotmp[m23] = fin1[m20] * fin2[m03] + fin1[m21] * fin2[m13] + fin1[m22] * fin2[m23] + fin1[m23] * fin2[m33];
|
|
|
|
fotmp[m30] = fin1[m30] * fin2[m00] + fin1[m31] * fin2[m10] + fin1[m32] * fin2[m20] + fin1[m33] * fin2[m30];
|
|
fotmp[m31] = fin1[m30] * fin2[m01] + fin1[m31] * fin2[m11] + fin1[m32] * fin2[m21] + fin1[m33] * fin2[m31];
|
|
fotmp[m32] = fin1[m30] * fin2[m02] + fin1[m31] * fin2[m12] + fin1[m32] * fin2[m22] + fin1[m33] * fin2[m32];
|
|
fotmp[m33] = fin1[m30] * fin2[m03] + fin1[m31] * fin2[m13] + fin1[m32] * fin2[m23] + fin1[m33] * fin2[m33];
|
|
#else
|
|
for( i = 0; i < 16; i++ )
|
|
{
|
|
int xp = i & 0x03;
|
|
int yp = i & 0x0c;
|
|
fotmp[i] = 0;
|
|
for( k = 0; k < 4; k++ )
|
|
{
|
|
fotmp[i] += fin1[yp+k] * fin2[(k<<2)|xp];
|
|
}
|
|
}
|
|
#endif
|
|
tdMATCOPY( fout, fotmp );
|
|
}
|
|
|
|
#ifndef __wasm__
|
|
void tdPrint( const float * f )
|
|
{
|
|
int i;
|
|
printf( "{\n" );
|
|
#ifdef CNFG3D_USE_OGL_MAJOR
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
printf( " %f, %f, %f, %f\n", f[0+i], f[4+i], f[8+i], f[12+i] );
|
|
}
|
|
#else
|
|
for( i = 0; i < 16; i+=4 )
|
|
{
|
|
printf( " %f, %f, %f, %f\n", f[0+i], f[1+i], f[2+i], f[3+i] );
|
|
}
|
|
#endif
|
|
printf( "}\n" );
|
|
}
|
|
#endif
|
|
|
|
void tdTransposeSelf( float * f )
|
|
{
|
|
float fout[16];
|
|
fout[m00] = f[m00]; fout[m01] = f[m10]; fout[m02] = f[m20]; fout[m03] = f[m30];
|
|
fout[m10] = f[m01]; fout[m11] = f[m11]; fout[m12] = f[m21]; fout[m13] = f[m31];
|
|
fout[m20] = f[m02]; fout[m21] = f[m12]; fout[m22] = f[m22]; fout[m23] = f[m32];
|
|
fout[m30] = f[m03]; fout[m31] = f[m13]; fout[m32] = f[m23]; fout[m33] = f[m33];
|
|
tdMATCOPY( f, fout );
|
|
}
|
|
|
|
|
|
void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out )
|
|
{
|
|
float f = 1./tdTAN(fovy * tdQ_PI / 360.0);
|
|
out[m00] = f/aspect; out[m01] = 0; out[m02] = 0; out[m03] = 0;
|
|
out[m10] = 0; out[m11] = f; out[m12] = 0; out[m13] = 0;
|
|
out[m20] = 0; out[m21] = 0;
|
|
out[m22] = (zFar + zNear)/(zNear - zFar);
|
|
out[m23] = 2*zFar*zNear /(zNear - zFar);
|
|
out[m30] = 0; out[m31] = 0; out[m32] = -1; out[m33] = 0;
|
|
}
|
|
|
|
void tdLookAt( float * m, float * eye, float * at, float * up )
|
|
{
|
|
float out[16];
|
|
float F[3] = { at[0] - eye[0], at[1] - eye[1], at[2] - eye[2] };
|
|
float fdiv = 1./tdSQRT( F[0]*F[0] + F[1]*F[1] + F[2]*F[2] );
|
|
float f[3] = { F[0]*fdiv, F[1]*fdiv, F[2]*fdiv };
|
|
float udiv = 1./tdSQRT( up[0]*up[0] + up[1]*up[1] + up[2]*up[2] );
|
|
float UP[3] = { up[0]*udiv, up[1]*udiv, up[2]*udiv };
|
|
float s[3];
|
|
float u[3];
|
|
tdCross( f, UP, s );
|
|
tdCross( s, f, u );
|
|
|
|
out[m00] = s[0]; out[m01] = s[1]; out[m02] = s[2]; out[m03] = 0;
|
|
out[m10] = u[0]; out[m11] = u[1]; out[m12] = u[2]; out[m13] = 0;
|
|
out[m20] = -f[0];out[m21] =-f[1]; out[m22] =-f[2]; out[m23] = 0;
|
|
out[m30] = 0; out[m31] = 0; out[m32] = 0; out[m33] = 1;
|
|
|
|
tdMultiply( m, out, m );
|
|
tdTranslate( m, -eye[0], -eye[1], -eye[2] );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void tdPTransform( const float * pin, float * f, float * pout )
|
|
{
|
|
float ptmp[2];
|
|
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + f[m03];
|
|
ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + f[m13];
|
|
pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + f[m23];
|
|
pout[0] = ptmp[0];
|
|
pout[1] = ptmp[1];
|
|
}
|
|
|
|
void tdVTransform( const float * pin, float * f, float * pout )
|
|
{
|
|
float ptmp[2];
|
|
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02];
|
|
ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12];
|
|
pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22];
|
|
pout[0] = ptmp[0];
|
|
pout[1] = ptmp[1];
|
|
}
|
|
|
|
void td4Transform( float * pin, float * f, float * pout )
|
|
{
|
|
float ptmp[3];
|
|
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + pin[3] * f[m03];
|
|
ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + pin[3] * f[m13];
|
|
ptmp[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + pin[3] * f[m23];
|
|
pout[3] = pin[0] * f[m30] + pin[1] * f[m31] + pin[2] * f[m32] + pin[3] * f[m33];
|
|
pout[0] = ptmp[0];
|
|
pout[1] = ptmp[1];
|
|
pout[2] = ptmp[2];
|
|
}
|
|
|
|
void td4RTransform( float * pin, float * f, float * pout )
|
|
{
|
|
float ptmp[3];
|
|
ptmp[0] = pin[0] * f[m00] + pin[1] * f[m10] + pin[2] * f[m20] + pin[3] * f[m30];
|
|
ptmp[1] = pin[0] * f[m01] + pin[1] * f[m11] + pin[2] * f[m21] + pin[3] * f[m31];
|
|
ptmp[2] = pin[0] * f[m02] + pin[1] * f[m12] + pin[2] * f[m22] + pin[3] * f[m32];
|
|
pout[3] = pin[0] * f[m03] + pin[1] * f[m13] + pin[2] * f[m23] + pin[3] * f[m33];
|
|
pout[0] = ptmp[0];
|
|
pout[1] = ptmp[1];
|
|
pout[2] = ptmp[2];
|
|
}
|
|
|
|
void tdNormalizeSelf( float * vin )
|
|
{
|
|
float vsq = 1./tdSQRT(vin[0]*vin[0] + vin[1]*vin[1] + vin[2]*vin[2]);
|
|
vin[0] *= vsq;
|
|
vin[1] *= vsq;
|
|
vin[2] *= vsq;
|
|
}
|
|
|
|
void tdCross( float * va, float * vb, float * vout )
|
|
{
|
|
float vtmp[2];
|
|
vtmp[0] = va[1] * vb[2] - va[2] * vb[1];
|
|
vtmp[1] = va[2] * vb[0] - va[0] * vb[2];
|
|
vout[2] = va[0] * vb[1] - va[1] * vb[0];
|
|
vout[0] = vtmp[0];
|
|
vout[1] = vtmp[1];
|
|
}
|
|
|
|
float tdDistance( float * va, float * vb )
|
|
{
|
|
float dx = va[0]-vb[0];
|
|
float dy = va[1]-vb[1];
|
|
float dz = va[2]-vb[2];
|
|
|
|
return tdSQRT(dx*dx + dy*dy + dz*dz);
|
|
}
|
|
|
|
float tdDot( float * va, float * vb )
|
|
{
|
|
return va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2];
|
|
}
|
|
|
|
//Stack functionality.
|
|
static float gsMatricies[2][tdMATRIXMAXDEPTH][16];
|
|
float * gSMatrix = gsMatricies[0][0];
|
|
static int gsMMode;
|
|
static int gsMPlace[2];
|
|
|
|
void tdPush()
|
|
{
|
|
if( gsMPlace[gsMMode] > tdMATRIXMAXDEPTH - 2 )
|
|
return;
|
|
|
|
tdMATCOPY( gsMatricies[gsMMode][gsMPlace[gsMMode] + 1], gsMatricies[gsMMode][gsMPlace[gsMMode]] );
|
|
gsMPlace[gsMMode]++;
|
|
|
|
gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]];
|
|
}
|
|
|
|
void tdPop()
|
|
{
|
|
if( gsMPlace[gsMMode] < 1 )
|
|
return;
|
|
|
|
gsMPlace[gsMMode]--;
|
|
|
|
gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]];
|
|
|
|
}
|
|
|
|
void tdMode( int mode )
|
|
{
|
|
if( mode < 0 || mode > 1 )
|
|
return;
|
|
|
|
gsMMode = mode;
|
|
|
|
gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]];
|
|
|
|
}
|
|
|
|
static float translateX;
|
|
static float translateY;
|
|
static float scaleX;
|
|
static float scaleY;
|
|
|
|
void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy )
|
|
{
|
|
translateX = leftx;
|
|
translateY = bottomy;
|
|
scaleX = pixx/(rightx-leftx);
|
|
scaleY = pixy/(topy-bottomy);
|
|
|
|
}
|
|
|
|
void tdFinalPoint( float * pin, float * pout )
|
|
{
|
|
float tdin[4] = { pin[0], pin[1], pin[2], 1. };
|
|
float tmp[4];
|
|
td4Transform( tdin, gsMatricies[0][gsMPlace[0]], tmp );
|
|
// printf( "XFORM1Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] );
|
|
td4Transform( tmp, gsMatricies[1][gsMPlace[1]], tmp );
|
|
// printf( "XFORM2Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] );
|
|
pout[0] = (tmp[0]/tmp[3] - translateX) * scaleX;
|
|
pout[1] = (tmp[1]/tmp[3] - translateY) * scaleY;
|
|
pout[2] = tmp[2]/tmp[3];
|
|
// printf( "XFORMFOut: %f %f %f\n", pout[0], pout[1], pout[2] );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float tdNoiseAt( int x, int y )
|
|
{
|
|
return ((x*13241*y + y * 33455927)%9293) / 4646. - 1.0;
|
|
}
|
|
|
|
static inline float tdFade( float f )
|
|
{
|
|
float ft3 = f*f*f;
|
|
return ft3 * 10 - ft3 * f * 15 + 6 * ft3 * f * f;
|
|
}
|
|
|
|
float tdFLerp( float a, float b, float t )
|
|
{
|
|
float fr = tdFade( t );
|
|
return a * (1.-fr) + b * fr;
|
|
}
|
|
|
|
static inline float tdFNoiseAt( float x, float y )
|
|
{
|
|
int ix = x;
|
|
int iy = y;
|
|
float fx = x - ix;
|
|
float fy = y - iy;
|
|
|
|
float a = tdNoiseAt( ix, iy );
|
|
float b = tdNoiseAt( ix+1, iy );
|
|
float c = tdNoiseAt( ix, iy+1 );
|
|
float d = tdNoiseAt( ix+1, iy+1 );
|
|
|
|
float top = tdFLerp( a, b, fx );
|
|
float bottom = tdFLerp( c, d, fx );
|
|
|
|
return tdFLerp( top, bottom, fy );
|
|
}
|
|
|
|
float tdPerlin2D( float x, float y )
|
|
{
|
|
int ndepth = 5;
|
|
|
|
int depth;
|
|
float ret = 0;
|
|
for( depth = 0; depth < ndepth; depth++ )
|
|
{
|
|
float nx = x / (1<<(ndepth-depth-1));
|
|
float ny = y / (1<<(ndepth-depth-1));
|
|
ret += tdFNoiseAt( nx, ny ) / (1<<(depth+1));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|