While trying to figure out CLIM, I ran into this example program. It's a simple maze game. The author claims to have tested it in LispWorks (and even has #+Genera in there, implying that this program would work on a real Lisp Machine), but I'm trying to get it working in SBCL with McCLIM.
Under SBCL/McCLIM, the window draws, but nothing visible happens when you press the movement keys. Non-movement keys cause text to be entered into the pane with the game instructions.
I figured out that the game command keys are changing the game's internal state, so the only problem is that the screen does not update.
Then I realized that you couldn't write code to redraw the maze from the scope of the code that implements the commands. All the methods that draw receive a stream argument from CLIM, which must be passed to the graphics primitives. For example:
(defun draw-stone (stream x y cell-width cell-height)
(let ((half-cell-width (/ cell-width 2))
(half-cell-height (/ cell-height 2)))
(draw-ellipse* stream
(+ (* x cell-width) half-cell-width)
(+ (* y cell-height) half-cell-height)
half-cell-width 0
0 half-cell-height
:ink +red+)))
But the code that handles keystrokes receives no stream argument:
(defmacro define-stone-move-command (name keystroke dx dy)
`(define-maze-frame-command (,name :keystroke ,keystroke) ()
(let ((maze-array (maze-array *application-frame*)))
(move-stone maze-array ,dx ,dy)
(check-for-win maze-array))))
What I ended up having to do is to save the stream argument from the first (and only) call to draw-maze-array to a global variable, so that I could add update code to the define-stone-command macro as follows:
(defmacro define-stone-move-command (name keystroke dx dy)
`(define-maze-frame-command (,name :keystroke ,keystroke) ()
(let ((maze-array (maze-array *application-frame*)))
(move-stone maze-array ,dx ,dy)
(check-for-win maze-array)
(draw-maze-array *application-frame* *maze-stream*))))
This slight alteration gives the desired behavior on SBCL with McCLIM, but this doesn't seem right, however. After all, the author claimed that the code worked fine on LispWorks. I have a few questions:
Can somebody who has LispWorks confirm that this program works as-is on LispWorks?
Does my alteration to the code make it fail on LispWorks?
What is the accepted way to handle screen updating in CLIM applications?
Drawing the maze in the command is not the right approach. Putting a maze-stream into a global variable is also bad. ;-)
The display pane has a :display-function. The idea is that after a command the whole application frame gets updated automagically. For example for :display-time :command-loop, the display pane would be updated automagically, after a command runs. There are other ways to update panes, but in this case a keystroke runs a command and then the top-level-loop would just call the display-function for each applicable pane. The default toplevel-loop reads a command (via mouse, command lines, keystrokes, ...), executes it and updates the application frame - in a loop.
The whole redisplay thing is extremely tricky/powerful. It allows from fully automagical redisplay mechanisms to extremely fine-grained control.
You can read about it here: CLIM 2 Spec. Note: there might be quite a bit difference between the spec and what implementations provide...
Related
By accident, I recently came across a latent coding error in one of my functions, dealing with a when statement. A reduced paraphrase might look like:
(defparameter a 0)
(when (or (= a 0)
(= a 1)
(* a a)))
The problem is a misplaced parenthesis, so it actually is
(when (or (= a 0)
(= a 1)
(* a a)))
In a situation like this, wouldn't it be useful for the compiler to generate either a style warning or note? It seems to me that the meaning of a when statement normally implies a condition and a body, even though the body is strictly optional. Of course, a print pretty would have caught this in the editor, but I had copied it from elsewhere. Is there a reason that SBCL does not check for these kinds of mistakes?
a print pretty would have caught this in the editor
To discuss the options, I know about:
trivial-formatter will format the source code.
(trivial-formatter:fmt :your-system :supersede)
cl-indentify indents the source code. Has a command line utility. I tried it once and it was not bad, but different than Emacs' indentation, thus annoying for me.
$ cl-indentify bar.lisp
It links to lispindent but I was less happy with its result.
However, the best would be to not only format the code and re-read it ourselves, but to
run checks against a set of rules to warn against code smells
This is what proposes the lisp-critic. It can critique a function or a file. However:
(edit) it doesn't really have a Slime integration, we have to either critique a function or a whole file.
if you feel adventurous, see an utility of mine here. It could be an easier way to test snippets that you enter at the REPL.
it hasn't the rule about when without a body (we can easily add it)
And it would be best that the run failed with an error status code if it found a code smell. Again, a little project of mine in beta tries to do that, see here. It doesn't have much rules now, but I just pushed a check for this. You can call the script:
$colisper.sh tests/playground.lisp
it shows an error (but doesn't write it in-place by default):
|;; when with no body
|(when (or (= a 0)
| (= a 1)
!| (* a a))
!| (error "colisper found a 'when' with a missing body. (we should error the script without this rewrite!)"))
and returns with an exit code, so we can use it has a git hook or on a CI pipeline.
The problem is that if a human writes (when x) (or whatever that expands into, perhaps (if x (progn) nil)) this is probably a mistake, but when a program writes it it may well not be: it may be just some edge case that the program hasn't been smart enough to optimize completely away. And a huge amount of code that the compiler processes is written by programs, not humans.
The code should run on Windows 10. I tried asking on Reddit, but the ideas are Unix/Linux only. There's also CFFI, but I didn't understand how to use it for this problem (the main usability part of the documentation I found is just an elaborate example not related to this problem).
I also looked through the SetCursorPos of Python, and found that it calls ctypes.windll.user32.SetCursorPos(x, y), but I have no clue what that would look like in CL.
And finally, there's CommonQt, but while there seems to be QtCursor::setPos in Qt, I couldn't find the CL version.
The function called by the Python example seems to be documented here. It is part of a shared library user32.dll, which you can load with CFFI,
(ql:quickload :cffi)
#+win32
(progn
(cffi:define-foreign-library user32
(:windows "user32.dll"))
(cffi:use-foreign-library user32))
The #+win32 means that this is only evaluated on Windows.
Then you can declare the foreign SetCursorPos-function with CFFI:DEFCFUN. According to the documentation, it takes in two ints and returns a BOOL. CFFI has a :BOOL-type, however the Windows BOOL seems to actually be an int. You could probably use cffi-grovel to automatically find the typedef from some Windows header, but I'll just use :INT directly here.
#+win32
(cffi:defcfun ("SetCursorPos" %set-cursor-pos) (:boolean :int)
(x :int)
(y :int))
I put a % in the name to indicate this is an internal function that should not be called directly (because it is only available on Windows). You should then write a wrapper that works on different platforms (actually implementing it on other platforms is left out here).
(defun set-cursor-pos (x y)
(check-type x integer)
(check-type y integer)
#+win32 (%set-cursor-pos x y)
#-win32 (error "Not supported on this platform"))
Now calling (set-cursor-pos 100 100) should move the mouse near the top left corner.
There are two problems here:
How to move the mouse in windows
How to call that function from CL.
It seems you have figured out a suitable win32 function exists so the challenge is to load the relevant library, declare the functions name and type, and then call it. I can’t really help you with that unfortunately.
Some other solutions you might try:
Write and compile a trivial C library to call the function you want and see if you can call that from CL (maybe this is easier?)
Write and compile a trivial C library and see if you can work out how to call it from CL
Write/find some trivial program in another language to move the mouse based on arguments/stdin and run that from CL
I'm using CLX+STUMPWM+McCLIM and when I modify the keyboard layout via "setxkbmap us -variant dvorak -option ctrl:nocaps" the keyboard layout fails to update in my CLIM applications, but updates correctly for everything else (thus, to use COLEMAK I run the appropriate shell command prior to starting up CLIM for the first time).
Thoughts on why this might be?
This appears to be a bug in CLX independently affecting McCLIM and stumpwm.
For instance, I'm testing the difference between
setxkbmap -layout us
(querty) and
setxkbmap -layout fr
(azerty). Running those commands doesn't affect neither stumpwm's input bar, nor Climacs. The default querty remains in effect.
X server sends keycodes to applications. Applications may interpret these keycodes using the keymap table, which they can request from the server.
It seems that in CLX the keycode to keysym transformation is carried out by the keycode->keysym function defined in translate.lisp. It calls the display-keyboard-mapping function defined right above it:
(defun display-keyboard-mapping (display)
(declare (type display display))
(declare (clx-values (simple-array keysym (display-max-keycode keysyms-per-keycode))))
(or (display-keysym-mapping display)
(setf (display-keysym-mapping display) (keyboard-mapping display))))
Apparently, this function only requests the keymap table once and caches it. Changing it to
(defun display-keyboard-mapping (display)
(declare (type display display))
(declare (clx-values (simple-array keysym (display-max-keycode keysyms-per-keycode))))
(setf (display-keysym-mapping display) (keyboard-mapping display)))
fixes both the input bar and Climacs. CAVEAT: I don't claim this doesn't break anything else.
NB: If attempting to run a shell command from stumpwm's input bar using the French layout, mind that ! is positioned on /.
There are a number of useful variables to control TAB key indentation and completion in Emacs (R) code chunks, when using ESS mode.
ess-tab-complete-in-script first indents lines and, if there is nothing to indent, autocompletes the word.
"With great power, comes great responsibility", so, when fast indenting your code, you might end up completing code without noticing with catastrophic results. Therefore you can refine things with the variable
ess-first-tab-never-complete. For example: if 'unless-eol, TAB completes only when cursor is at the end of the line; if 'symbol, it completes also in the middle of a line, but not if you are in the middle of a word; etc (read doc for more
with F1vess-first-tab-never-complete).
The problem is that, at least for me, TAB is bound to ess-noweb-indent-line, but the command governing indent-or-complete behaviour is: ess-indent-or-complete. So I use to fix the tab binding with:
(add-hook 'ess-mode-hook
'(lambda()
(local-set-key (kbd "<tab>") 'ess-indent-or-complete)
))
This works, but I wonder if this is the proper way. In the manual I don't see any prompt to hook to ESS mode and reset tab binding.
Do you know which is the canonical way to perform this?
There are a couple of reasonable ways to set the tab key (or any key, for that matter) for a specific mode. The first you alluded to in your answer, by setting the key locally via a mode hook. Note, though, that it's generally preferable to use a named function rather than a lambda so that you can remove the hook later if you want to do so:
(defun ess-keys-hook ()
"Put a bunch of keybindings in here."
(local-set-key [tab] 'ess-indent-or-complete))
(add-hook 'ess-mode-hook 'ess-keys-hook)
The other option is to define the key in the relevant mode map, which you can do like so:
(define-key ess-mode-map [tab] 'ess-indent-or-complete)
Both ways are pretty commonly used, although my own preference is for the latter, as it strikes me as cleaner and more efficient.
I'm using a local emacs instance (aquamacs) to run R processes on a remote server, and I'd like to automate the process of connecting to my server. The process is as follows:
[in emacs]
M-x shell
[in the resulting console]
TERM=xterm
ssh -Y -C <my remote server>
screen -rd [and/or] R
[in emacs]
M-x ess-remote
r
I discovered this general approach here: http://blog.nguyenvq.com/2010/07/11/using-r-ess-remote-with-screen-in-emacs/. The -Y -C options allow you use xterm to view plots. I don't know lisp and tho I've googled around a bit, I can't seem to piece together how to actually define a function to automate this (e.g., in .emacs.el). Has anyone implemented anything like this?
Let's assume you just want to call shell in code. In Lisp, everything is prefix notation surrounded by parentheses. So we enter this into a buffer (say, the scratch buffer):
(shell)
Move your pointer to the end of the line after the close-paren, and type <C-x C-e> to execute the Lisp code. You should see that the shell function is called.
Now, let's make it a function, so we can add other things to it. The command to create a function is defun, and it takes the name of the function, the argument list (in parentheses), and then the body of the function:
(defun automate-connection ()
(shell))
Move your cursor to the end of the code, hit <C-x C-e>, and the function will be defined. You can call it from Lisp by executing
(automate-connection)
Ok, now we just need to put some text into the shell buffer.
(defun automate-connection ()
(shell)
(insert "TERM=xterm"))
Now, when we run that, we get "TERM=xterm" put into the shell buffer. But it doesn't actually send the command. Let's try putting a newline.
(defun automate-connection ()
(shell)
(insert "TERM=xterm\n"))
That puts in a newline, but doesn't actually make the command run. Why not? Let's see what the enter key does. Go to your *shell* buffer, and type <C-h c>, then hit the return key. (<C-h c> runs describe-key-briefly, which prints the name of the function invoked by hitting the given key). That says that when you hit RET, it's not putting a newline, but actually calling comint-send-input. So let's do that:
(defun automate-connection ()
(shell)
(insert "TERM=xterm")
(comint-send-input))
Now, when you run `(automate-connection) from any Lisp code, you should get the given thing sent. I leave it as an exercise to the reader to add your other commands.
But wait! We're not really done, are we? I assume you don't want to have to move to a Lisp scratch buffer, type in (automate-connection), then evaluate that code. You probably just want to type , and call it a day. You can't do that by default with the function we just created. Luckily, it's simple to allow that: just add a call to (interactive) in your function:
(defun automate-connection ()
(interactive)
(shell)
(insert "TERM=xterm")
(comint-send-input))
Now you can call it as you want, and it'll open the *shell* buffer, put in the text, and tell Emacs to tell the shell to run that text.