The SYS statement allows you to call an API function either by name or by specifying its memory address (pointer). The latter method is a little faster, so when speed is important you may wish to discover, in the initialisation section of your program, the address(es) of any functions you later call, especially if you call them frequently. You can do that as follows:
You do not need to adopt this naming convention, but it is important that the address be assigned to a variable capable of holding a 64-bit pointer, i.e. either a 64-bit integer (%% suffix) or a numeric variant (no suffix).`SDL_SetWindowTitle` = SYS("SDL_SetWindowTitle")
title$ = "New title" SYS "SDL_SetWindowTitle", @hwnd%, title$, @memhdc%
The above program segment will load the variable xscreen% with the width of the desktop and the variable yscreen% with the height of the desktop, both in pixels.DIM rc{x%, y%, w%, h%} SYS "SDL_GetDisplayUsableBounds", 0, rc{}, @memhdc% xscreen% = rc.w% yscreen% = rc.h%
The flags% parameter determines what kind of symbol is displayed:message$ = "Test message" caption$ = "Test caption" SYS "SDL_ShowSimpleMessageBox", flags%, caption$, message$, @hwnd%, @memhdc%
Value Symbol 16 Error symbol 32 Warning symbol 64 Information symbol
The wav$ parameter should be set to the (path and) filename of the WAV file to be played, or to an empty string ("") to stop a previous file playing and to shut down the sound system. The mix% parameter should be set to zero to play the file normally (replacing any previous WAV file still being played), or to a non-zero value (1 to 128) to mix the new file with a currently-playing WAV at the specified level (128 corresponding to '100%').DEF FNplaywave(wav$, mix%) LOCAL R%, S%, r%%, s%% : PRIVATE Q%, q%%, audiospec{} IF wav$ = "" THEN IF @hwo% SYS "SDL_CloseAudioDevice", @hwo%, @memhdc% : @hwo% = 0 IF q%% SYS "SDL_FreeWAV", q%% : q%% = 0 : Q% = 0 = TRUE ENDIF DIM audiospec{freq%, fmt{l&,h&}, channels&, silence&, samples%, size%, cb%%, userdata%%} SYS "SDL_RWFromFile", wav$, "rb" TO r%% : IF @platform% AND &40 ELSE r%% = !^r%% IF r%% = 0 THEN = FALSE : REM Couldn't open WAV file SYS "SDL_LoadWAV_RW", r%%, 1, audiospec{}, ^s%%, ^S% IF s%% = 0 THEN = FALSE : REM Couldn't load WAV file IF @hwo% = 0 SYS "SDL_OpenAudioDevice", FALSE, FALSE, audiospec{}, 0, 0, @memhdc% TO @hwo% IF @hwo% = 0 THEN = FALSE : REM Couldn't open audio device SYS "SDL_GetQueuedAudioSize", @hwo%, @memhdc% TO R% IF mix% IF S% <= (Q% - R%) IF q%% THEN SYS "SDL_MixAudioFormat", q%% + Q% - R%, s%%, &FFFF AND !^audiospec.fmt.l&, S%, mix% SYS "SDL_FreeWAV", s%% : s%% = q%% + Q% - R% : S% = R% ELSE IF q%% SYS "SDL_FreeWAV", q%% q%% = s%% : Q% = S% ENDIF SYS "SDL_PauseAudioDevice", @hwo%, 1, @memhdc% SYS "SDL_ClearQueuedAudio", @hwo%, @memhdc% SYS "SDL_QueueAudio", @hwo%, s%%, S%, @memhdc% SYS "SDL_PauseAudioDevice", @hwo%, FALSE, @memhdc% = TRUE
Note that the mix facility only works if the remaining duration of the previously-playing WAV file is greater than or equal to the total duration of the new sound. Otherwise the function will behave as though mix% was zero (and there is no control over the level). It is most useful for playing short-duration sound effects over a background music track.
The function returns TRUE if the WAV file was opened and played successfully, and FALSE if not (for example the file could not be opened, or was in a format that wasn't recognised).
The value of tick% will be set to the number of milliseconds since BBCSDL was started (it wraps around to a negative value after approximately 24 days and 20 hours). If this could be an issue there is a 64-bit version of the function:SYS "SDL_GetTicks" TO tick%
SYS "SDL_GetTicks64" TO tick%%
or to use the TIME pseudo-variable:pause = INKEY(delay%)
However both of these methods have their disadvantages. The INKEY delay can be truncated by pressing a key, which may be undesirable, and the TIME method keeps the processor fully occupied, so other applications will run slowly whilst your program is paused. A better method is to use WAIT:TIME = 0 REPEAT UNTIL TIME >= delay%
This is probably the best method for long delays, but an alternative is to use the SDL_Delay function:WAIT delay%
The program will pause for approximately delay% milliseconds. Note that during this time the ESCape key is not tested, nor can the window be closed. Therefore this method should be used only for short delays.SYS "SDL_Delay", delay%
However be aware that it's possible that another thread has overwritten the message before you had a chance to read it.DEF FNsdlerror LOCAL message%% SYS "SDL_GetError" TO message%% IF @platform% AND &40 ELSE message%% = !^message%% = $$message%%
The position is specified as the offset, in pixels, from the top-left corner of the desktop to the top-left corner of BASIC's output window. To change the window's size without moving it you can do the following:SYS "SDL_SetWindowPosition", @hwnd%, xpos%, ypos%, @memhdc%
The width and height are specified in pixels, including the border and the title bar. If want to specify the dimensions excluding the border and title bar, i.e. of the region usable by BASIC, then the best way of doing it is to select a user-defined mode with VDU 23,22....SYS "SDL_SetWindowSize", @hwnd%, width%, height%, @memhdc% VDU 26
Whenever you intentionally change the window size, you should reset BASIC's text and graphics clipping regions to the new size by issuing the VDU 26 command.
To discover the current size of the window you can use the SDL_GL_GetDrawableSize function, which returns the width and height in pixels:
This gives the usable size of the window, i.e. excluding the title bar etc.SYS "SDL_GL_GetDrawableSize", @hwnd%, ^Width%, ^Height%, @memhdc%
SYS "SDL_SetWindowResizable", @hwnd%, FALSE, @memhdc%
This will have exactly the same effect as clicking on the minimise button in the title bar. To restore the window to its original size and position:SYS "SDL_MinimizeWindow", @hwnd%, @memhdc%
To maximise the window (equivalent to clicking on the maximise button):SYS "SDL_RestoreWindow", @hwnd%, @memhdc%
If you maximise the window you should normally use a VDU 26 to reset the text and graphics viewports so they fill the full area.SYS "SDL_MaximizeWindow", @hwnd%, @memhdc% VDU 26
SYS "SDL_RaiseWindow", @hwnd%, @memhdc%
As this removes the close button, make sure you provide an obvious means for quitting the program (Alt-F4 will still work so long as you haven't used ON CLOSE).SYS "SDL_SetWindowBordered", @hwnd%, FALSE, @memhdc%
When you run your program not even the taskbar will be visible, nor will the user be able to re-size or move the window, so make sure you provide an obvious means for quitting the program (Alt-F4 will still work so long as you haven't used ON CLOSE).SYS "SDL_SetWindowFullscreen", @hwnd%, &1001, @memhdc% VDU 26
To restore windowed output do:
SYS "SDL_SetWindowFullscreen", @hwnd%, 0, @memhdc% VDU 26
To do this incorporate an ON MOVE interrupt as follows (typically during the initialisation phase of your program, but after setting the initial window size with MODE or VDU 23,22...):
and include the following procedure, for example at the end of your program:IF POS REM Thread sync to ensure @size{} is not 'stale' ON MOVE PROCresize(@msg%, @lparam%, @size.x%, @size.y%) : RETURN
Note: Do not include a VDU 26 in your program, it is important that the logical size of the 'screen' (output canvas) remains fixed at its original value.DEF PROCresize(M%, L%, X%, Y%) IF M% <> 5 ENDPROC LOCAL W%, H% W% = L% AND &FFFF H% = L% >>> 16 IF W%/H% > X%/Y% THEN @zoom% = &8000 * H% / Y% ELSE @zoom% = &8000 * W% / X% ENDIF IF @zoom% < &8000 @zoom% = &8000 IF (@platform% AND 7) < 3 THEN @panx% = (X% - W% * &8000 / @zoom%) / 2 @pany% = (Y% - H% * &8000 / @zoom%) / 2 ENDIF ENDPROC
To check whether the clipboard contains any text, you can use the following function:text$ = "The five boxing wizards jump quickly"+CHR$13+CHR$10 SYS "SDL_SetClipboardText", text$
which will set res% to 1 if there is text in the clipboard and to 0 otherwise. Once you have established that there is text available, you can read it with the following function:SYS "SDL_HasClipboardText" TO res%
The SDL 2.0 clipboard always uses UTF-8 encoding.DEF FNgetclipboardtext LOCAL t%% SYS "SDL_GetClipboardText" TO t%% IF @platform% AND &40 ELSE t%% = !^t%% IF t%% = 0 THEN = "" = $$t%%
But if you want to read part of a file directly into memory rather than via a string, you can do that as follows:
Note the use of the system variable @hfile%() to discover the SDL 2.0 file context.DIM store%% size%-1 file% = OPENIN(filename$) PTR#file% = offset%% SYS "SDL_RWread", @hfile%(file%), store%%, 1, size% CLOSE #file%
Similarly to write part of a file you can use BPUT #file,a$; to write the contents of a string, but if you want to write directly from memory rather than via a string you can do that as follows:
DIM store%% size%-1 file% = OPENUP(filename$) PTR#file% = offset%% SYS "SDL_RWwrite", @hfile%(file%), store%%, 1, size% CLOSE #file%
If you need to call this routine multiple times ensure that you move the DIM statement so that it will be executed only once. If you don't, you may eventually run out of memory. Alternatively, consider using DIM LOCAL.
The following program segment restores the priority to its normal value:SYS "SDL_SetThreadPriority", 2
You should use this facility with extreme care, because while the priority of your program is raised other programs may run very slowly or not at all. You may not even be able to stop or interrupt your own program! You should raise the priority only for the shortest possible period, and then return it to normal.SYS "SDL_SetThreadPriority", 1
SYS "SDL_GetWindowFlags", @hwnd%, @memhdc% TO flags% IF flags% AND &200 THEN REM program has input focus ENDIF
Where R%, G%, B% are the colour of the text (each 0-255), X% and Y% are the coordinates of the centre of the text (graphics units) and the angle of rotation is specified in degrees clockwise.INSTALL @lib$ + "gfxlib" PROC_gfxInit ... DEF PROCangled(text$, R%, G%, B%, X%, Y%, angle) LOCAL W%, H%, t%% IF POS REM SDL thread sync X% = (X%+@vdu%!0)>>1 Y% = @vdu%!212-((Y%+@vdu%!4)>>1) t%% = FN_gfxCreateTextureFromText(text$, R%, G%, B%, W%, H%) PROC_gfxPlotRotateScale(t%%, W%, H%, X%, Y%, angle) PROC_gfxDestroyTexture(t%%) ENDPROC
The text is drawn in the currently selected font, with a transparent background (as it would be in VDU 5 mode).
The parameter url$ should be set to the URL of the file or page you want to download; this must be a regular (http://) URL, not an encrypted (https://) URL. The second parameter should be set to a timeout value, in centiseconds, after which the function will return even if the file has not been fully read.DEF FNhttpget(url$, T%) LOCAL b$, I%, S%, host$, path$, ret$ b$ = STRING$(256, CHR$0) S% = INSTR(url$,"//")+2 : IF S%=2 S%=1 I% = INSTR(url$,"/",S%) host$ = MID$(url$,S%,I%-S%) path$ = MID$(url$,I%) S% = FN_tcpconnect(host$, "80") IF S% = FALSE OR S% = TRUE THEN = "" I% = FN_writelinesocket(S%,"GET "+path$+" HTTP/1.0") I% = FN_writelinesocket(S%,"Host: "+host$) I% = FN_writelinesocket(S%,"User-agent: BBC BASIC") I% = FN_writelinesocket(S%,"Accept: */*") I% = FN_writelinesocket(S%,"") T% += TIME REPEAT I% = FN_readsocket(S%,PTR(b$),256) IF I% = 0 WAIT 1 IF I% > 0 ret$ += LEFT$(b$,I%) UNTIL I% < 0 OR TIME > T% PROC_closesocket(S%) CASE VALMID$(ret$,9,255) OF WHEN 200: I% = INSTR(ret$,CHR$&D+CHR$&A+CHR$&D+CHR$&A) IF I% ret$ = MID$(ret$,I%+4) WHEN 301: I% = INSTR(ret$,"Location:") IF I%=0 I% = INSTR(ret$,"location:") IF I% ret$ = FNhttpget("http://" + host$ + $(PTR(ret$) + I% + 9), T% - TIME) ENDCASE = ret$
The requested file is returned as a string; you can easily write it to a local file if you wish. If an empty string is returned the file could not be read within the specified timeout period (e.g. you are not connected to the internet).
The xhot% and yhot% parameters are the coordinates of the pointer's 'hot spot' (offsets in pixels, from the top-left corner, of where it is 'pointing').DEF PROCcreatecustompointer(png$, xhot%, yhot%) LOCAL surface%%, cursor%% SYS "STBIMG_Load", png$ TO surface%% IF surface%% = 0 ERROR 100, "Couldn't load image " + png$ SYS "SDL_CreateColorCursor", surface%%, xhot%, yhot%, @memhdc% TO cursor%% IF cursor%% = 0 ERROR 100, "Couldn't create custom cursor" SYS "SDL_SetCursor", cursor%%, @memhdc% SYS "SDL_FreeSurface", surface%% ENDPROC
CONTENTS |
CONTINUE |