M cl-octaspire-csfml.asd => cl-octaspire-csfml.asd +1 -0
@@ 22,6 22,7 @@
:pathname "src/"
:serial t
:components ((:file "package")
+ (:file "stdlib")
(:file "system"))
:in-order-to ((test-op (test-op :cl-octaspire-csfml/test))))
M doc/API.org => doc/API.org +8 -0
@@ 1,6 1,7 @@
* CSFML
** TODO System
Helper function ~CLAMP~ is available.
+Function ~memcpy~ from C standard library is available.
*** DONE Clock.h
**** DONE Function ~sfClock_create~
Exported as ~CLOCK-CREATE~.
@@ 15,6 16,13 @@ Helper function ~CLAMP~ is available.
*** DONE Export.h
Nothing to do for Lisp side.
*** TODO InputStream.h
+**** DONE Structure ~sfInputStream~
+ ~sfInputStream~ is defined for Lisp, but is not exported. It has
+ four function pointer members: ~MREAD~, ~MSEEK~, ~MTELL~, and ~MGETSIZE~.
+ And also pointer member ~MUSERDATA~. Use exported symbol ~:INPUTSTREAM~ instead.
+ So instead of writing ~(:STRUCT SFINPUTSTREAM)~, please write ~:INPUTSTREAM~.
+ Additional constructor ~MAKE-INPUTSTREAM~ and destructor ~RELEASE-INPUTSTREAM~
+ are available. Class ~MEMSTREAM~ can be used to read from memory buffer.
*** DONE Mutex.h
All skipped. Use Lisp instead.
*** DONE Sleep.h
M src/package.lisp => src/package.lisp +10 -1
@@ 21,6 21,8 @@
(:nicknames :csfml)
(:use :cl :cffi)
(:export
+ ;; STDLIB
+ :memcpy
;; SYSTEM
:vector2i :mx :my
:make-vector2i :release-vector2i
@@ 60,4 62,11 @@
:clock-copy
:clock-destroy
:clock-getelapsedtime
- :clock-restart))
+ :clock-restart
+ :inputstream :buffer :buflen :index
+ :inputstream-read :inputstream-seek :inputstream-tell :inputstream-getsize
+ :memstream
+ :make-memstream
+ :inputstream :mread :mseek :mtell :mgetsize :muserdata
+ :make-inputstream :release-inputstream
+ :read-cb :seek-cb :tell-cb :getsize-cb))
A src/stdlib.lisp => src/stdlib.lisp +26 -0
@@ 0,0 1,26 @@
+;; Copyright (c) 2022 octaspire.com
+;;
+;; 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
+;; 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.
+(in-package :cl-octaspire-csfml)
+
+(defcfun ("memcpy" memcpy) :pointer
+ "Copy LEN bytes from SRC to DST."
+ (dst :pointer)
+ (src :pointer)
+ (len :size))
M src/system.lisp => src/system.lisp +102 -0
@@ 415,3 415,105 @@ equal to all the rest given times (if only one value is given, returns true)."
;;
;; Skipped. Use Lisp instead.
+
+;;
+;; INPUTSTREAM
+;;
+;; Foreign CSFML types for this section are declared in file
+;; SFML/System/InputStream.h.
+;;
+
+;; INPUTSTREAM
+
+(defclass inputstream ()
+ ((buffer :accessor buffer :initarg :buffer)
+ (buflen :accessor buflen :initarg :buflen)
+ (index :accessor index :initarg :index))
+ (:documentation "Base class for input stream implementations."))
+
+(defgeneric inputstream-read (self target amount)
+ (:documentation "Read from stream SELF up to AMOUNT octets into TARGET."))
+
+(defgeneric inputstream-seek (self position)
+ (:documentation "Try to seek stream SELF to octet index POSITION."))
+
+(defgeneric inputstream-tell (self)
+ (:documentation "Return current octet index of stream SELF."))
+
+(defgeneric inputstream-getsize (self)
+ (:documentation "Return current octet size of stream SELF."))
+
+(defclass memstream (inputstream)
+ ()
+ (:documentation "Class for reading input from memory buffer."))
+
+(defun make-memstream (buffer)
+ "Constructor for creating and initializing MEMSTREAM."
+ (make-instance 'memstream :buffer buffer :buflen (length buffer) :index 0))
+
+(defmethod inputstream-read ((self memstream) target amount)
+ (let* ((available (- (buflen self) (index self)))
+ (actual (clamp amount 0 available)))
+ (with-foreign-objects ((c :char))
+ (dotimes (i actual)
+ (setf (mem-aref target :char i)
+ (aref (buffer self) (index self)))
+ (incf (index self)))
+ actual)))
+
+(defmethod inputstream-seek ((self memstream) position)
+ (setf (index self) (clamp position 0 (1- (buflen self)))))
+
+(defmethod inputstream-tell ((self memstream))
+ (index self))
+
+(defmethod inputstream-getsize ((self memstream))
+ (buflen self))
+
+(defcallback read-cb :int64
+ ((data :pointer)
+ (size :int64)
+ (userdata :pointer))
+ (stream-read *userdata* data size))
+
+(defcallback seek-cb :int64
+ ((position :int64)
+ (userdata :pointer))
+ (stream-seek *userdata* position))
+
+(defcallback tell-cb :int64
+ ((userdata :pointer))
+ (stream-tell *userdata*))
+
+(defcallback getsize-cb :int64
+ ((userdata :pointer))
+ (stream-getsize *userdata*))
+
+(defcstruct (sfinputstream :class sfinputstream-type)
+ "Holds callbacks that allow user to define custom input streams.
+Symbol for this type is not exported, please use public type :INPUTSTREAM instead.
+So instead of writing (:STRUCT SFINPUTSTREAM), please write :INPUTSTREAM.
+Foreign CSFML type is defined in file SFML/System/InputStream.h."
+ (mread :pointer)
+ (mseek :pointer)
+ (mtell :pointer)
+ (mgetsize :pointer)
+ (muserdata :pointer))
+
+(defctype :inputstream (:struct sfinputstream)
+ "Public type intended to be used instead of (:STRUCT SFINPUTSTREAM).")
+
+(defun make-inputstream (read seek tell getsize userdata)
+ "Constructor for allocating and initializing new :INPUTSTREAM."
+ (let ((v (foreign-alloc :inputstream)))
+ (with-foreign-slots ((mread mseek mtell mgetsize muserdata) v :inputstream)
+ (setf mread read
+ mseek seek
+ mtell tell
+ mgetsize getsize
+ muserdata userdata))
+ (return-from make-inputstream v)))
+
+(defun release-inputstream (self)
+ "Release memory allocated for :INPUTSTREAM SELF."
+ (foreign-free self))
M test/system-test.lisp => test/system-test.lisp +59 -0
@@ 676,3 676,62 @@ was expected.")))))
;; THREAD
;; Skipped Use Lisp instead.
+
+;;
+;; INPUTSTREAM
+;;
+;; Foreign CSFML types for this section are declared in file
+;; SFML/System/InputStream.h.
+;;
+
+;; INPUTSTREAM
+
+(defparameter *userdata* nil)
+
+(fiveam:test
+ test-make-inputstream--release-inputstream
+ (let* ((read (callback read-cb))
+ (seek (callback seek-cb))
+ (tell (callback tell-cb))
+ (getsize (callback getsize-cb))
+ (stream (make-memstream #(1 2 3)))
+ (v (make-inputstream read seek tell getsize (null-pointer))))
+ (setf *userdata* stream)
+ (with-foreign-slots ((mread mseek mtell mgetsize muserdata) v :inputstream)
+ (is (pointer-eq read mread)
+ #?"READ component is $(mread) when $(read) was expected.")
+ (is (pointer-eq seek mseek)
+ #?"SEEK component is $(mseek) when $(seek) was expected.")
+ (is (pointer-eq tell mtell)
+ #?"TELL component is $(mtell) when $(tell) was expected.")
+ (is (pointer-eq getsize mgetsize)
+ #?"GETSIZE component is $(mgetsize) when $(getsize) was expected."))
+ (is (= 3 (inputstream-getsize stream))
+ (format nil
+ "INPUTSTREAM-GETSIZE returned ~A, when 3 was expected."
+ (inputstream-getsize stream)))
+ (is (= 0 (inputstream-tell stream))
+ (format nil
+ "INPUTSTREAM-TELL returned ~A, when 0 was expected."
+ (inputstream-tell stream)))
+ (with-foreign-object (target :char 1)
+ (let ((octets-read (inputstream-read stream target 1))
+ (value-saved (mem-aref target :char 0)))
+ (is (= 1 octets-read) #?"One octet was expected, but got $(octets-read)")
+ (is (= 1 value-saved) #?"Octet was $(value-saved), when 1 was expected.")))
+ (is (= 1 (inputstream-tell stream))
+ (format nil
+ "INPUTSTREAM-TELL returned ~A, when 1 was expected."
+ (inputstream-tell stream)))
+ (with-foreign-object (target :char 2)
+ (let ((octets-read (inputstream-read stream target 2))
+ (value-saved-1 (mem-aref target :char 0))
+ (value-saved-2 (mem-aref target :char 1)))
+ (is (= 2 octets-read) #?"Two octets were expected, but got $(octets-read)")
+ (is (= 2 value-saved-1) #?"First octet was $(value-saved-1), when 2 was expected.")
+ (is (= 3 value-saved-2) #?"Second octet was $(value-saved-2), when 3 was expected.")))
+ (is (= 3 (inputstream-tell stream))
+ (format nil
+ "INPUTSTREAM-TELL returned ~A, when 3 was expected."
+ (inputstream-tell stream)))
+ (release-inputstream v)))