The context
I'm using the after-make-frame-functions
hook to load properly the themes in an emacs client/server configuration. Specifically this is the code snippet that I use to make that (based in this SO answer):
(if (daemonp)
(add-hook 'after-make-frame-functions
(lambda (frame)
(select-frame frame)
(load-theme 'monokai t)
;; setup the smart-mode-line and its theme
(sml/setup)))
(progn (load-theme 'monokai t)
(sml/setup)))
The problem
When a new emacsclient -c/t
session is started the hook is executed not only in the new frame, but in all the previous existent frames (other emacsclient sessions) and it makes a very annoying visual effect (the themes are loaded again in all those frames). Even worse, in the terminal clients already opened the theme color get completely messed up. Obviously that happens only on the emacs clients connected to the same emacs server. The reason for this behavior is clear, the hook is run on the server and all of its clients are affected.
The question
Is there any way to execute this function only once or getting the same result without using the hook?
A partial solution
I have now this code, thanks to the @Drew's answer. But still has a problem, once you start a client session in the terminal, the GUI does not load the themes properly and vice-versa. After a lot of tests, I realized that the behavior depends on which emacsclient starts first, and after discard various things, I think that it is maybe related with the color palette that is loaded. If you reload manually the theme all works fine and that's the reason why this behavior does not appear when the function is called by the hook every time as in my initial configuration.
(defun emacsclient-setup-theme-function (frame)
(progn
(select-frame frame)
(load-theme 'monokai t)
;; setup the smart-mode-line and its theme
(sml/setup)
(remove-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)))
(if (daemonp)
(add-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)
(progn (load-theme 'monokai t)
(sml/setup)))
The final solution
Finally I have totally working code that solves the behavior seen in the partial solution, to achieve this I run the function once by mode (terminal or gui) when then pertinent emacsclient is started for first time, then remove the function from the hook because is no needed any more. Now, I am happy! :) Thanks again @Drew!
The code:
(setq myGraphicModeHash (make-hash-table :test 'equal :size 2))
(puthash "gui" t myGraphicModeHash)
(puthash "term" t myGraphicModeHash)
(defun emacsclient-setup-theme-function (frame)
(let ((gui (gethash "gui" myGraphicModeHash))
(ter (gethash "term" myGraphicModeHash)))
(progn
(select-frame frame)
(when (or gui ter)
(progn
(load-theme 'monokai t)
;; setup the smart-mode-line and its theme
(sml/setup)
(sml/apply-theme 'dark)
(if (display-graphic-p)
(puthash "gui" nil myGraphicModeHash)
(puthash "term" nil myGraphicModeHash))))
(when (not (and gui ter))
(remove-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)))))
(if (daemonp)
(add-hook 'after-make-frame-functions 'emacsclient-setup-theme-function)
(progn (load-theme 'monokai t)
(sml/setup)))