8

I am trying to have specific settings when I start a new emacs frame in X as opposed to in the terminal while using emacs-server. My initial attempt was using this solution, but it appears it isn't working for me (using Emacs 24.4.1).

Basically, my minimimal non-working example is this:

(defun new-frame-setup (&optional frame)
  (if (display-graphic-p)
      (message "window system")
    (message "not a window system")
    ))

;; run when regular emacs is started
(new-frame-setup)
;; run when a new frame is created using server
(add-hook 'after-make-frame-functions 'new-frame-setup)

When I start regular emacs and go to my Messages buffer it says:

window system

So this works just as I expected. However, if I run the command emacsclient -c, the Messages buffer says

not a window system
Starting Emacs daemon
not a window system

The first "not a window system" makes sense, as it's starting the emacs server in the terminal. However, the second one doesn't make sense, as that frame is already a graphical window. Furthermore, after the fact, if I evaluate (display-graphic-p) in my scratch buffer, it evaluates as t. Any ideas as to what I'm doing wrong?

EDIT

So the big problem here is not that (display-graphic-p) doesn't work, it's that it doesn't know what frame to check. This was a problem for me since the daemon didn't have a window-system, but it's an even bigger problem if I have a terminal version and an X version of emacsclient running at the same time. For example, if I create a frame with emacsclient -c and have it change some settings, and then I create a frame with emacsclient -nw and have it change some other settings - all of those settings are getting changed at the global level.

So I guess the real question is: how can I get emacs to check the display of the most recently created frame, and then run some elisp code only on that frame? I have absolutely no idea if this is possible.

rottweiler
  • 83
  • 5
  • Try focus-in-hook instead. – Kaushal Modi Jul 14 '16 at 19:54
  • That somewhat works, but I really only want these setup functions to run once. For example, when i start an emacs client, i have it move to my left monitor - if i use focus-in-hook, it will make it unable to move my window after the fact. – rottweiler Jul 14 '16 at 20:09
  • Did you try using the variable window-system instead? – theldoria Jul 14 '16 at 20:11
  • using window-system gives the exact same problem. I used display-graphic-p because according to the docs "Use of this variable as a boolean is deprecated" – rottweiler Jul 14 '16 at 20:18
  • 1
    @rottweiler You can then remove that function from that hook from within that function. It sounds crazy, but works. :) – Kaushal Modi Jul 14 '16 at 20:28
  • @KaushalModi wow... now that is a unique solution and is pretty close! It does have two shortcomings for me though: it works great if i run emacsclient -c myfile, but without running it with the file argument, it doesn't immediately focus on the new client (so it moves after i click it). Second, this doesn't let me run separate code when i run it in the terminal. The display-graphic-p lets me use an if-else to run separate code for the different instances. – rottweiler Jul 14 '16 at 21:07
  • Actually, I can fix the first problem by calling (raise-frame) in the function. I think the second problem might not be a problem either, since all the "terminal" code will get called whenever i start emacs-server. – rottweiler Jul 14 '16 at 21:15
  • @rottweiler Right (to your 2nd last comment), the focus-in-hook is to be used in place of after-make-frame-functions. You still need to use display-graphic-p to know if you are running in terminal. But then focus-in-hook is not run when you run emacs[client] with -nw. So to cover that case, you need to add your fn to after-init-hook. You might also use (daemonp) to do something like this. <- In this example, after-make-frame-functions does what I need. – Kaushal Modi Jul 14 '16 at 21:17
  • Sorry if I'm not understanding, but does that means I won't be able to run code specifically for emacs[client] with -nw? i.e. i would need to put something in an after-init-hook so that it gets run every time, and then selectively disable things in my focus-in-hook? – rottweiler Jul 15 '16 at 15:35
  • Ahhh, so I see the problem: see my edit above – rottweiler Jul 15 '16 at 16:27

1 Answers1

6

The hook on after-make-frame-functions runs after the frame is created but before it's selected (if it ever becomes selected). So you're asking whether the currently selected frame is on a graphical display, rather than whether the newly created frame is on a graphical display. This is easy to fix: pass the frame you're interested in to display-graphic-p.

(defun new-frame-setup (frame)
  (if (display-graphic-p frame)
      (message "window system")
    (message "not a window system")))

;; Run for already-existing frames
(mapc 'new-frame-setup (frame-list))
;; Run when a new frame is created
(add-hook 'after-make-frame-functions 'new-frame-setup)
  • This correctly answers my question. Even better (for my case) is to add &optional before frame, that way i can also have (unless (daemonp) (add-hook 'after-init-hook 'new-frame-setup)) for when i run emacs or emacs -nw. I do have a follow up question though: I believe any code that gets run in new-frame-setup is run globally, therefore I don't believe the mapc line does much. Is there a way to make something run only on a single frame and not globally? (This may be worth a posting a new question) – rottweiler Jul 16 '16 at 18:39
  • @rottweiler I'm not sure I understand your follow-up question. You can run (new-frame-setup some-frame). The reason I used mapc was to make the code more robust: it runs on every existing frame. You don't need any special handling for a daemon or non-daemon startup, and you don't need to use after-init-hook. – Gilles 'SO- stop being evil' Jul 16 '16 at 20:02
  • The main reason I want to do this is so that I can set specific settings (keybindings, mouse settings, line numbering, etc.) for emacsclient -c and emacsclient -nw, much like I currently do when I have separate instances of emacs/emacs-nw running. But for emacsserver, the main functionality of this fix is to change specific frame characteristics (like positioning and size) which are a small subset of functions. Here, if start a terminal version of emacsclient and then open a X version of emacsclient, all those new keybindings/etc get applied globally to all open frames. – rottweiler Jul 16 '16 at 21:49
  • I found it useful to read add-to-list vs add-hook and the Emacs description of hooks, which explains why we use add-hook here instead of add-to-list. We know after-make-frame-functions is a "hook" (an "abnormal" hook) because its name ends with "-functions". And we use add-hook on hooks both to make code clearly self-descriptive of what we mean, and because add-hook knows how to handle edge cases like the hook variable being nil. – mtraceur Mar 30 '23 at 23:00