jue
jue

Reputation: 477

sdl2:load-bmp Problem with current working directory, common-lisp

While trying to work through cl-sdl2-tutorial, I'm having trouble loading a bitmap due to a wrong current working directory.
I'd like to get a proper solution to the problem using relative path names.

A minimal working example:
Having modified the code of example two from above mentioned tutorial.

(defpackage #:sdl2-tutorial-2
  (:use :common-lisp)
  (:export :main))

(in-package :sdl2-tutorial-2)

(defparameter *screen-width* 640)
(defparameter *screen-height* 480)

(defmacro with-window-surface ((window surface) &body body)
  `(sdl2:with-init (:video)
     (sdl2:with-window (,window
                        :title "SDL2 Tutorial"
                        :w *screen-width*
                        :h *screen-height*
                        :flags '(:shown))
       (let ((,surface (sdl2:get-window-surface ,window)))
         ,@body))))

(defun main(&key (delay 2000))
  (format t " cwd: ~a, ~% dpd: ~a, ~& e-p: ~a, ~% pf: ~a, ~& load: ~a"
          (sb-posix:getcwd)
          *default-pathname-defaults*
          (uiop:file-exists-p "hello_world.bmp")
          (probe-file "hello_world.bmp")
          (sdl2:load-bmp "hello_world.bmp"))
  
  (with-window-surface (window screen-surface)
    (let ((image (sdl2:load-bmp "hello_world.bmp")))
      (break "1 here with ~a~%" image)
      (setf image (sdl2:load-bmp "hello_world.bmp"))
      (break "2 here with ~a~%" image)
      (break "3 error: ~a~%" (sdl2-ffi.functions:sdl-get-error))
      (sdl2:blit-surface image
                         nil
                         screen-surface
                         nil)
      (sdl2:update-window window)
      (sdl2:with-event-loop (:method :poll)
        (:quit () t)
        (:idle ()
               (sdl2:delay delay))))))

Before compiling above code and running (main), I changed working directory in the REPL, via:

(sb-posix:chdir (truename "/test/cl-sdl2-tutorial/2/"))
(setf *default-pathname-defaults* (truename "/test/cl-sdl2-tutorial/2/"))

The above code prints, as expected, when running (main) in the REPL:

SDL2-TUTORIAL-2> (sdl2-tutorial-2:main)
  0: (SDL2-TUTORIAL-2:MAIN)
 cwd: /test/cl-sdl2-tutorial/2, 
 dpd: /test/cl-sdl2-tutorial/2/, 
 e-p: /test/cl-sdl2-tutorial/2/hello_world.bmp, 
 pf: /test/cl-sdl2-tutorial/2/hello_world.bmp, 
 load: #<SDL-SURFACE {#X7F5CBC018DD0}>

Problem:
The bitmap can not be found and therefore not loaded.
Calls to (sdl2:load-bmp "hello_world.bmp") always return a a zero pointer (#<SDL-SURFACE {#X00000000}>) and breakpoint 3 states:

3 error: Couldn't open /home/jue/hello_world.bmp

but evaling (sdl2:load-bmp "hello_world.bmp") during a break from breakpoints 1 or 2 or 3, is successful and continuing (main) displays the picture.

Questions:

Remarks (I'm using current released versions of sbcl, Emacs, sly on a Linux machine, if that matters. I'm only intermediate experienced with Common Lisp and its development environment, but advanced at elisp)

Upvotes: 0

Views: 312

Answers (1)

user5920214
user5920214

Reputation:

I suspect but don't know that the problem is that the sdl2 library is doing fanciness with threads, with the result that the working directory isn't what you think.

The solution to this in my experience is never to let the implementation second-guess you like that. In any case where there's some interface which says "do something to a file" give it an absolute pathname so it has no chance to do any thinking of its own. Do something like.

(defparameter *where-my-bitmaps-live* (merge-pathnames
                                       (pathname "lib/bitmaps/")
                                       (user-homedir-pathname)))

...

(defun load-a-bitmap (name)
  (load-bmp (merge-pathnames (pathname name) *where-my-bitmaps-live*)))

And now it really has no excuse.

(You want to check that the above pathname-mergery is actually right: it seems to be for me but I forget the details of pathname rules every time I look away for more than a few minutes).

Upvotes: 1

Related Questions