Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2012 George Nachman <tmux (at) georgester.com>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 
     21 #include <stdlib.h>
     22 #include <string.h>
     23 
     24 #include "tmux.h"
     25 
     26 struct notify_entry {
     27 	const char		*name;
     28 	struct cmd_find_state	 fs;
     29 	struct format_tree	*formats;
     30 
     31 	struct client		*client;
     32 	struct session		*session;
     33 	struct window		*window;
     34 	int			 pane;
     35 	const char		*pbname;
     36 };
     37 
     38 static struct cmdq_item *
     39 notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne,
     40     struct cmd_list *cmdlist, struct cmdq_state *state)
     41 {
     42 	struct cmdq_item	*new_item;
     43 	char			*s;
     44 
     45 	if (cmdlist == NULL)
     46 		return (item);
     47 	if (log_get_level() != 0) {
     48 		s = cmd_list_print(cmdlist, 0);
     49 		log_debug("%s: hook %s is: %s", __func__, ne->name, s);
     50 		free(s);
     51 	}
     52 	new_item = cmdq_get_command(cmdlist, state);
     53 	return (cmdq_insert_after(item, new_item));
     54 }
     55 
     56 static void
     57 notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
     58 {
     59 	struct cmd_find_state		 fs;
     60 	struct options			*oo;
     61 	struct cmdq_state		*state;
     62 	struct options_entry		*o;
     63 	struct options_array_item	*a;
     64 	struct cmd_list			*cmdlist;
     65 	const char			*value;
     66 	struct cmd_parse_result		*pr;
     67 
     68 	log_debug("%s: inserting hook %s", __func__, ne->name);
     69 
     70 	cmd_find_clear_state(&fs, 0);
     71 	if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
     72 		cmd_find_from_nothing(&fs, 0);
     73 	else
     74 		cmd_find_copy_state(&fs, &ne->fs);
     75 
     76 	if (fs.s == NULL)
     77 		oo = global_s_options;
     78 	else
     79 		oo = fs.s->options;
     80 	o = options_get(oo, ne->name);
     81 	if (o == NULL && fs.wp != NULL) {
     82 		oo = fs.wp->options;
     83 		o = options_get(oo, ne->name);
     84 	}
     85 	if (o == NULL && fs.wl != NULL) {
     86 		oo = fs.wl->window->options;
     87 		o = options_get(oo, ne->name);
     88 	}
     89 	if (o == NULL) {
     90 		log_debug("%s: hook %s not found", __func__, ne->name);
     91 		return;
     92 	}
     93 
     94 	state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS);
     95 	cmdq_add_formats(state, ne->formats);
     96 
     97 	if (*ne->name == '@') {
     98 		value = options_get_string(oo, ne->name);
     99 		pr = cmd_parse_from_string(value, NULL);
    100 		switch (pr->status) {
    101 		case CMD_PARSE_ERROR:
    102 			log_debug("%s: can't parse hook %s: %s", __func__,
    103 			    ne->name, pr->error);
    104 			free(pr->error);
    105 			break;
    106 		case CMD_PARSE_SUCCESS:
    107 			notify_insert_one_hook(item, ne, pr->cmdlist, state);
    108 			break;
    109 		}
    110 	} else {
    111 		a = options_array_first(o);
    112 		while (a != NULL) {
    113 			cmdlist = options_array_item_value(a)->cmdlist;
    114 			item = notify_insert_one_hook(item, ne, cmdlist, state);
    115 			a = options_array_next(a);
    116 		}
    117 	}
    118 
    119 	cmdq_free_state(state);
    120 }
    121 
    122 static enum cmd_retval
    123 notify_callback(struct cmdq_item *item, void *data)
    124 {
    125 	struct notify_entry	*ne = data;
    126 
    127 	log_debug("%s: %s", __func__, ne->name);
    128 
    129 	if (strcmp(ne->name, "pane-mode-changed") == 0)
    130 		control_notify_pane_mode_changed(ne->pane);
    131 	if (strcmp(ne->name, "window-layout-changed") == 0)
    132 		control_notify_window_layout_changed(ne->window);
    133 	if (strcmp(ne->name, "window-pane-changed") == 0)
    134 		control_notify_window_pane_changed(ne->window);
    135 	if (strcmp(ne->name, "window-unlinked") == 0)
    136 		control_notify_window_unlinked(ne->session, ne->window);
    137 	if (strcmp(ne->name, "window-linked") == 0)
    138 		control_notify_window_linked(ne->session, ne->window);
    139 	if (strcmp(ne->name, "window-renamed") == 0)
    140 		control_notify_window_renamed(ne->window);
    141 	if (strcmp(ne->name, "client-session-changed") == 0)
    142 		control_notify_client_session_changed(ne->client);
    143 	if (strcmp(ne->name, "client-detached") == 0)
    144 		control_notify_client_detached(ne->client);
    145 	if (strcmp(ne->name, "session-renamed") == 0)
    146 		control_notify_session_renamed(ne->session);
    147 	if (strcmp(ne->name, "session-created") == 0)
    148 		control_notify_session_created(ne->session);
    149 	if (strcmp(ne->name, "session-closed") == 0)
    150 		control_notify_session_closed(ne->session);
    151 	if (strcmp(ne->name, "session-window-changed") == 0)
    152 		control_notify_session_window_changed(ne->session);
    153 	if (strcmp(ne->name, "paste-buffer-changed") == 0)
    154 		control_notify_paste_buffer_changed(ne->pbname);
    155 	if (strcmp(ne->name, "paste-buffer-deleted") == 0)
    156 		control_notify_paste_buffer_deleted(ne->pbname);
    157 
    158 	notify_insert_hook(item, ne);
    159 
    160 	if (ne->client != NULL)
    161 		server_client_unref(ne->client);
    162 	if (ne->session != NULL)
    163 		session_remove_ref(ne->session, __func__);
    164 	if (ne->window != NULL)
    165 		window_remove_ref(ne->window, __func__);
    166 
    167 	if (ne->fs.s != NULL)
    168 		session_remove_ref(ne->fs.s, __func__);
    169 
    170 	format_free(ne->formats);
    171  	free(__UNCONST(ne->name));
    172 	free(__UNCONST(ne->pbname));
    173 	free(ne);
    174 
    175 	return (CMD_RETURN_NORMAL);
    176 }
    177 
    178 static void
    179 notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
    180     struct session *s, struct window *w, struct window_pane *wp,
    181     const char *pbname)
    182 {
    183 	struct notify_entry	*ne;
    184 	struct cmdq_item	*item;
    185 
    186 	item = cmdq_running(NULL);
    187 	if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
    188 		return;
    189 
    190 	ne = xcalloc(1, sizeof *ne);
    191 	ne->name = xstrdup(name);
    192 
    193 	ne->client = c;
    194 	ne->session = s;
    195 	ne->window = w;
    196 	ne->pane = (wp != NULL ? (int)wp->id : -1);
    197 	ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
    198 
    199 	ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
    200 	format_add(ne->formats, "hook", "%s", name);
    201 	if (c != NULL)
    202 		format_add(ne->formats, "hook_client", "%s", c->name);
    203 	if (s != NULL) {
    204 		format_add(ne->formats, "hook_session", "$%u", s->id);
    205 		format_add(ne->formats, "hook_session_name", "%s", s->name);
    206 	}
    207 	if (w != NULL) {
    208 		format_add(ne->formats, "hook_window", "@%u", w->id);
    209 		format_add(ne->formats, "hook_window_name", "%s", w->name);
    210 	}
    211 	if (wp != NULL)
    212 		format_add(ne->formats, "hook_pane", "%%%d", wp->id);
    213 	format_log_debug(ne->formats, __func__);
    214 
    215 	if (c != NULL)
    216 		c->references++;
    217 	if (s != NULL)
    218 		session_add_ref(s, __func__);
    219 	if (w != NULL)
    220 		window_add_ref(w, __func__);
    221 
    222 	cmd_find_copy_state(&ne->fs, fs);
    223 	if (ne->fs.s != NULL) /* cmd_find_valid_state needs session */
    224 		session_add_ref(ne->fs.s, __func__);
    225 
    226 	cmdq_append(NULL, cmdq_get_callback(notify_callback, ne));
    227 }
    228 
    229 void
    230 notify_hook(struct cmdq_item *item, const char *name)
    231 {
    232 	struct cmd_find_state	*target = cmdq_get_target(item);
    233 	struct notify_entry	 ne;
    234 
    235 	memset(&ne, 0, sizeof ne);
    236 
    237 	ne.name = name;
    238 	cmd_find_copy_state(&ne.fs, target);
    239 
    240 	ne.client = cmdq_get_client(item);
    241 	ne.session = target->s;
    242 	ne.window = target->w;
    243 	ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
    244 
    245 	ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
    246 	format_add(ne.formats, "hook", "%s", name);
    247 	format_log_debug(ne.formats, __func__);
    248 
    249 	notify_insert_hook(item, &ne);
    250 	format_free(ne.formats);
    251 }
    252 
    253 void
    254 notify_client(const char *name, struct client *c)
    255 {
    256 	struct cmd_find_state	fs;
    257 
    258 	cmd_find_from_client(&fs, c, 0);
    259 	notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
    260 }
    261 
    262 void
    263 notify_session(const char *name, struct session *s)
    264 {
    265 	struct cmd_find_state	fs;
    266 
    267 	if (session_alive(s))
    268 		cmd_find_from_session(&fs, s, 0);
    269 	else
    270 		cmd_find_from_nothing(&fs, 0);
    271 	notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
    272 }
    273 
    274 void
    275 notify_winlink(const char *name, struct winlink *wl)
    276 {
    277 	struct cmd_find_state	fs;
    278 
    279 	cmd_find_from_winlink(&fs, wl, 0);
    280 	notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
    281 }
    282 
    283 void
    284 notify_session_window(const char *name, struct session *s, struct window *w)
    285 {
    286 	struct cmd_find_state	fs;
    287 
    288 	cmd_find_from_session_window(&fs, s, w, 0);
    289 	notify_add(name, &fs, NULL, s, w, NULL, NULL);
    290 }
    291 
    292 void
    293 notify_window(const char *name, struct window *w)
    294 {
    295 	struct cmd_find_state	fs;
    296 
    297 	cmd_find_from_window(&fs, w, 0);
    298 	notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
    299 }
    300 
    301 void
    302 notify_pane(const char *name, struct window_pane *wp)
    303 {
    304 	struct cmd_find_state	fs;
    305 
    306 	cmd_find_from_pane(&fs, wp, 0);
    307 	notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
    308 }
    309 
    310 void
    311 notify_paste_buffer(const char *pbname, int deleted)
    312 {
    313   	struct cmd_find_state	fs;
    314 
    315 	cmd_find_clear_state(&fs, 0);
    316 	if (deleted) {
    317 		notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
    318 		    pbname);
    319 	} else {
    320 		notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
    321 		    pbname);
    322 	}
    323 }
    324