From 9174a8f1c9cfb247869f581d1a699dfa85a315ea Mon Sep 17 00:00:00 2001 From: Shpoike Date: Wed, 8 Jul 2020 15:38:08 +0100 Subject: [PATCH] Custom hardware cursors, fix missing cursor when running fullscreen. --- quakespasm/Quake/gl_vidsdl.c | 68 +++++++++++++++++++++++++++ quakespasm/Quake/in_sdl.c | 15 +++++- quakespasm/Quake/mathlib.h | 6 +-- quakespasm/Quake/pr_ext.c | 91 +++++++++++------------------------- quakespasm/Quake/progs.h | 2 + quakespasm/Quake/vid.h | 3 ++ 6 files changed, 115 insertions(+), 70 deletions(-) diff --git a/quakespasm/Quake/gl_vidsdl.c b/quakespasm/Quake/gl_vidsdl.c index 2151018b..85bcb749 100644 --- a/quakespasm/Quake/gl_vidsdl.c +++ b/quakespasm/Quake/gl_vidsdl.c @@ -75,6 +75,7 @@ static int nummodes; static qboolean vid_initialized = false; +static SDL_Cursor *vid_cursor; #if defined(USE_SDL2) static SDL_Window *draw_context; static SDL_GLContext gl_context; @@ -2346,3 +2347,70 @@ static void VID_Menu_f (void) VID_Menu_RebuildRateList (); } +void VID_UpdateCursor(void) +{ + SDL_Cursor *nc; + + qcvm_t *vm; + if (key_dest == key_menu) + vm = &cls.menu_qcvm; + else if (key_dest == key_game) + vm = &cl.qcvm; + else + vm = NULL; + nc = vm?vm->cursorhandle:NULL; + if (vid_cursor != nc) + { + vid_cursor = nc; + if (nc) //null is an invalid sdl cursor handle + SDL_SetCursor(nc); + else + SDL_SetCursor(SDL_GetDefaultCursor()); //doesn't need freeing or anything. + } +} +void VID_SetCursor(qcvm_t *vm, const char *cursorname, float hotspot[2], float cursorscale) +{ + SDL_Cursor *oldcursor; + int mark = Hunk_LowMark(); + qboolean malloced = false; + char npath[MAX_QPATH]; + SDL_Surface *surf = NULL; + byte *imagedata = NULL; + if (cursorname && *cursorname) + { + enum srcformat fmt; + int width, height; + COM_StripExtension(cursorname, npath, sizeof(npath)); + imagedata = Image_LoadImage(npath, &width, &height, &fmt, &malloced); + if (imagedata && fmt == SRC_RGBA) + { //simple 32bit RGBA byte-ordered data. + surf = SDL_CreateRGBSurfaceFrom(imagedata, width, height, 32, width*4, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + if (cursorscale != 1 && surf) + { //rescale image by cursorscale + int nwidth = q_max(1,width*cursorscale); + int nheight = q_max(1,height*cursorscale); + SDL_Surface *scaled = SDL_CreateRGBSurface(0, nwidth, nheight, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + SDL_BlitScaled(surf, NULL, scaled, NULL); + SDL_FreeSurface(surf); + surf = scaled; + } + } + } + + oldcursor = vm->cursorhandle; + if (surf) + { + vm->cursorhandle = SDL_CreateColorCursor(surf, hotspot[0], hotspot[1]); + SDL_FreeSurface(surf); + } + else + vm->cursorhandle = NULL; + + Hunk_FreeToLowMark(mark); + if (malloced) + free(imagedata); + + VID_UpdateCursor(); + if (oldcursor) + SDL_FreeCursor(oldcursor); +} \ No newline at end of file diff --git a/quakespasm/Quake/in_sdl.c b/quakespasm/Quake/in_sdl.c index bffd7efa..d81fb1f5 100644 --- a/quakespasm/Quake/in_sdl.c +++ b/quakespasm/Quake/in_sdl.c @@ -289,8 +289,9 @@ static void IN_UpdateGrabs_Internal(qboolean forecerelease) qboolean freemouse; //the OS should have a free cursor too... qboolean needevents; //whether we want to receive events still - wantcursor = (key_dest == key_console || (key_dest == key_menu&&!bind_grab)) || (key_dest == key_game && cl.qcvm.cursorforced); - freemouse = wantcursor && (modestate == MS_WINDOWED || (key_dest == key_game && cl.qcvm.cursorforced)); + qboolean gamecodecursor = (key_dest == key_game && cl.qcvm.cursorforced) || (key_dest == key_menu && cls.menu_qcvm.cursorforced); + wantcursor = (key_dest == key_console || (key_dest == key_menu&&!bind_grab)) || gamecodecursor; + freemouse = wantcursor && (modestate == MS_WINDOWED || gamecodecursor); needevents = (!wantcursor) || key_dest == key_game; if (isDedicated) @@ -310,6 +311,16 @@ static void IN_UpdateGrabs_Internal(qboolean forecerelease) #endif #if defined(USE_SDL2) + if (wantcursor) + { + VID_UpdateCursor(); + SDL_ShowCursor(SDL_ENABLE); + } + else + { + SDL_ShowCursor(SDL_DISABLE); + VID_UpdateCursor(); + } if (SDL_SetRelativeMouseMode(freemouse?SDL_FALSE:SDL_TRUE) != 0) { Con_Printf("WARNING: SDL_SetRelativeMouseMode(%s) failed.\n", freemouse?"SDL_FALSE":"SDL_TRUE"); diff --git a/quakespasm/Quake/mathlib.h b/quakespasm/Quake/mathlib.h index c01e1f6c..ca5da705 100644 --- a/quakespasm/Quake/mathlib.h +++ b/quakespasm/Quake/mathlib.h @@ -54,9 +54,9 @@ static inline int IS_NAN (float x) { #define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) #define DotProduct2(x,y) (x[0]*y[0]+x[1]*y[1]) #define DoublePrecisionDotProduct(x,y) ((double)x[0]*y[0]+(double)x[1]*y[1]+(double)x[2]*y[2]) -#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];} -#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} -#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} +#define VectorSubtract(a,b,c) (c[0]=a[0]-b[0],c[1]=a[1]-b[1],c[2]=a[2]-b[2]) +#define VectorAdd(a,b,c) (c[0]=a[0]+b[0],c[1]=a[1]+b[1],c[2]=a[2]+b[2]) +#define VectorCopy(a,b) (b[0]=a[0],b[1]=a[1],b[2]=a[2]) //johnfitz -- courtesy of lordhavoc // QuakeSpasm: To avoid strict aliasing violations, use a float/int union instead of type punning. diff --git a/quakespasm/Quake/pr_ext.c b/quakespasm/Quake/pr_ext.c index 45132cbf..86d34441 100644 --- a/quakespasm/Quake/pr_ext.c +++ b/quakespasm/Quake/pr_ext.c @@ -37,7 +37,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern cvar_t sv_gameplayfix_setmodelrealbox; cvar_t pr_checkextension = {"pr_checkextension", "1", CVAR_NONE}; //spike - enables qc extensions. if 0 then they're ALL BLOCKED! MWAHAHAHA! *cough* *splutter* -int pr_ext_warned_particleeffectnum; //so these only spam once per map +static int pr_ext_warned_particleeffectnum; //so these only spam once per map static void *PR_FindExtGlobal(int type, const char *name); void SV_CheckVelocity (edict_t *ent); @@ -1349,7 +1349,7 @@ static struct { unsigned int start; unsigned int end; } qctoken[MAXQCTOKENS]; -unsigned int qctoken_count; +static unsigned int qctoken_count; static void tokenize_flush(void) { @@ -1379,7 +1379,7 @@ static int tokenizeqc(const char *str, qboolean dpfuckage) while (qctoken_count < MAXQCTOKENS) { /*skip whitespace here so the token's start is accurate*/ - while (*str && *(unsigned char*)str <= ' ') + while (*str && *(const unsigned char*)str <= ' ') str++; if (!*str) @@ -2900,7 +2900,7 @@ static void PF_fsize(void) } #endif -struct filesearch_s +static struct filesearch_s { qcvm_t *owner; size_t numfiles; @@ -3061,7 +3061,7 @@ struct strbuf { #define BUFSTRBASE 1 #define NUMSTRINGBUFS 64u -struct strbuf strbuflist[NUMSTRINGBUFS]; +static struct strbuf strbuflist[NUMSTRINGBUFS]; static void PF_buf_shutdown(void) { @@ -3184,11 +3184,11 @@ static void PF_buf_copy(void) static int PF_buf_sort_sortprefixlen; static int PF_buf_sort_ascending(const void *a, const void *b) { - return strncmp(*(char**)a, *(char**)b, PF_buf_sort_sortprefixlen); + return strncmp(*(char*const*)a, *(char*const*)b, PF_buf_sort_sortprefixlen); } static int PF_buf_sort_descending(const void *b, const void *a) { - return strncmp(*(char**)a, *(char**)b, PF_buf_sort_sortprefixlen); + return strncmp(*(char*const*)a, *(char*const*)b, PF_buf_sort_sortprefixlen); } // #444 void(float bufhandle, float sortprefixlen, float backward) buf_sort (DP_QC_STRINGBUFFERS) static void PF_buf_sort(void) @@ -4161,7 +4161,7 @@ static void PF_crc16(void) G_FLOAT(OFS_RETURN) = crc; } else - G_FLOAT(OFS_RETURN) = CRC_Block((byte*)str, len); + G_FLOAT(OFS_RETURN) = CRC_Block((const byte*)str, len); } static void PF_digest_hex(void) { @@ -4619,14 +4619,14 @@ static void PF_cl_getstat_string(void) } } -struct +static struct { char name[MAX_QPATH]; int type; qpic_t *pic; } *qcpics; -size_t numqcpics; -size_t maxqcpics; +static size_t numqcpics; +static size_t maxqcpics; void PR_ReloadPics(qboolean purge) { numqcpics = 0; @@ -4902,8 +4902,8 @@ static void PF_cl_drawfill(void) static qpic_t *polygon_pic; #define MAX_POLYVERTS -polygonvert_t polygon_verts[256]; -unsigned int polygon_numverts; +static polygonvert_t polygon_verts[256]; +static unsigned int polygon_numverts; static void PF_R_PolygonBegin(void) { qpic_t *pic = DrawQC_CachePic(G_STRING(OFS_PARM0), false); @@ -5087,37 +5087,11 @@ static void PF_cl_findkeysforcommandex(void) static void PF_cl_setcursormode(void) { qboolean absmode = G_FLOAT(OFS_PARM0); -// const char *cursorname = (qcvm->argc<=1)?"":G_STRING(OFS_PARM1); -// float *hotspot = (qcvm->argc<=2)?NULL:G_VECTOR(OFS_PARM2); -// float cursorscale = (qcvm->argc<=3)?1:G_FLOAT(OFS_PARM3); - -/* if (absmode) - { - int mark = Hunk_LowMark(); - int width, height; - qboolean malloced; - byte *imagedata = Image_LoadImage(cursorname, &width, &height, &malloced); - //TODO: rescale image by cursorscale - SDL_Surface *surf = !imagedata?NULL:SDL_CreateRGBSurfaceFrom(imagedata, width, height, 32, width*4, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); - Hunk_FreeToLowMark(mark); - if (malloced) - free(imagedata); - if (surf) - { - cursor = SDL_CreateColorCursor(surf, hotspot[0], hotspot[1]); - SDL_FreeSurface(surf); - SDL_SetCursor(cursor); - } - else - { - SDL_SetCursor(SDL_GetDefaultCursor()); - cursor = NULL; - } - if (oldcursor) - SDL_FreeCursor(oldcursor); - oldcursor = cursor; - }*/ + const char *cursorname = (qcvm->argc<=1)?"":G_STRING(OFS_PARM1); + float *hotspot = (qcvm->argc<=2)?NULL:G_VECTOR(OFS_PARM2); + float cursorscale = (qcvm->argc<=3)?1:G_FLOAT(OFS_PARM3); + VID_SetCursor(qcvm, cursorname, hotspot, cursorscale); qcvm->cursorforced = absmode; } static void PF_cl_getcursormode(void) @@ -5391,40 +5365,25 @@ static void PF_m_getmousepos(void) } static void PF_m_setmousetarget(void) { - /*int d = G_FLOAT(OFS_PARM0); + int d = G_FLOAT(OFS_PARM0); switch(d) { - case 0: - key_dest = key_game; - break; case 1: - key_dest = key_message; + qcvm->cursorforced = false; break; case 2: - key_dest = key_menu; + qcvm->cursorforced = true; break; default: break; - }*/ + } } static void PF_m_getmousetarget(void) { - G_FLOAT(OFS_RETURN) = 0; - /*switch(key_dest) - { - case key_game: - G_FLOAT(OFS_RETURN) = 0; - break; - case key_message: - G_FLOAT(OFS_RETURN) = 1; - break; - case key_menu: + if (qcvm->cursorforced) G_FLOAT(OFS_RETURN) = 2; - break; - default: - G_FLOAT(OFS_RETURN) = 0; - break; - }*/ + else + G_FLOAT(OFS_RETURN) = 1; } static void PF_cl_getresolution(void) { @@ -6321,6 +6280,7 @@ static struct {"soundlength", PF_NoSSQC, PF_cl_soundlength, 534, PF_cl_soundlength,534, D("float(string sample)", "Provides a way to query the duration of a sound sample, allowing you to set up a timer to chain samples.")}, {"buf_loadfile", PF_buf_loadfile, PF_buf_loadfile, 535, PF_buf_loadfile,535, D("float(string filename, strbuf bufhandle)", "Appends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable.")}, {"buf_writefile", PF_buf_writefile, PF_buf_writefile, 536, PF_buf_writefile,536, D("float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings)", "Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer.")}, + //{"bufstr_find", PF_bufstr_find, PF_bufstr_find, 537, PF_bufstr_find,537, D("float(strbuf bufhandle, string match, float matchtype=5, optional float firstidx=0, optional float step=1)", "Finds the first occurence of the requested string, returning its index (or -1).")}, {"setkeydest", PF_NoSSQC, PF_NoCSQC, 601, PF_m_setkeydest,601, D("void(float dest)", "Grab key focus")}, {"getkeydest", PF_NoSSQC, PF_NoCSQC, 602, PF_m_getkeydest,602, D("float()", "Returns key focus")}, @@ -6436,6 +6396,7 @@ static const char *extnames[] = #endif "FTE_QC_CHECKCOMMAND", "FTE_QC_CROSSPRODUCT", + "FTE_QC_HARDWARECURSORS", "FTE_QC_INFOKEY", "FTE_QC_INTCONV", "FTE_QC_MULTICAST", diff --git a/quakespasm/Quake/progs.h b/quakespasm/Quake/progs.h index c48081f4..d7f18d5a 100644 --- a/quakespasm/Quake/progs.h +++ b/quakespasm/Quake/progs.h @@ -287,7 +287,9 @@ struct qcvm_s struct pr_extglobals_s extglobals; struct pr_extfuncs_s extfuncs; struct pr_extfields_s extfields; + qboolean cursorforced; + void *cursorhandle; //video code. //was static inside pr_edict char *strings; diff --git a/quakespasm/Quake/vid.h b/quakespasm/Quake/vid.h index afd96b8c..c873b76e 100644 --- a/quakespasm/Quake/vid.h +++ b/quakespasm/Quake/vid.h @@ -91,5 +91,8 @@ qboolean VID_IsMinimized (void); void VID_Lock (void); void VID_SetWindowCaption(const char *newcaption); +void VID_UpdateCursor(void); +void VID_SetCursor(qcvm_t *vm, const char *cursorname, float hotspot[2], float cursorscale); + #endif /* __VID_DEFS_H */