1 /* vifm
2  * Copyright (C) 2014 xaizek.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "cancellation.h"
20 
21 #include <curses.h> /* noraw() raw() */
22 
23 #include <assert.h> /* assert() */
24 
25 #include "../utils/cancellation.h"
26 #include "../utils/int_stack.h"
27 #include "../status.h"
28 
29 /* State of cancellation request processing. */
30 typedef enum
31 {
32 	CRS_DISABLED,           /* Cancellation is disabled. */
33 	CRS_DISABLED_REQUESTED, /* Cancellation is disabled and was requested. */
34 	CRS_ENABLED,            /* Cancellation is enabled, but wasn't requested. */
35 	CRS_ENABLED_REQUESTED,  /* Cancellation is enabled and was requested. */
36 }
37 cancellation_request_state;
38 
39 static int ui_cancellation_hook(void *arg);
40 static int ui_cancellation_enabled(void);
41 static int is_enabled(cancellation_request_state state);
42 
43 const cancellation_t ui_cancellation_info = { .hook = &ui_cancellation_hook };
44 
45 /* Whether cancellation was requested.  Used by ui_cancellation_* group of
46  * functions. */
47 static cancellation_request_state cancellation_state;
48 
49 /* Previous cancellation states. */
50 static int_stack_t cancellation_stack;
51 
52 /* Implementation of cancellation hook for. */
53 static int
ui_cancellation_hook(void * arg)54 ui_cancellation_hook(void *arg)
55 {
56 	return ui_cancellation_requested();
57 }
58 
59 void
ui_cancellation_push_on(void)60 ui_cancellation_push_on(void)
61 {
62 	int_stack_push(&cancellation_stack, cancellation_state);
63 
64 	if(!ui_cancellation_enabled())
65 	{
66 		ui_cancellation_enable();
67 	}
68 
69 	cancellation_state = CRS_ENABLED;
70 }
71 
72 void
ui_cancellation_push_off(void)73 ui_cancellation_push_off(void)
74 {
75 	int_stack_push(&cancellation_stack, cancellation_state);
76 
77 	if(ui_cancellation_enabled())
78 	{
79 		ui_cancellation_disable();
80 	}
81 
82 	cancellation_state = CRS_DISABLED;
83 }
84 
85 void
ui_cancellation_enable(void)86 ui_cancellation_enable(void)
87 {
88 	/* TODO: consider enabling this assert:
89 	assert(!int_stack_is_empty(&cancellation_stack) && "Must push state first.");
90 	*/
91 	assert(!ui_cancellation_enabled() && "Can't enable twice in a row.");
92 
93 	cancellation_state = (cancellation_state == CRS_DISABLED)
94 	                   ? CRS_ENABLED
95 	                   : CRS_ENABLED_REQUESTED;
96 
97 	/* The check is here for tests, which are running with uninitialized
98 	 * curses. */
99 	if(curr_stats.load_stage > 2)
100 	{
101 		/* Temporary disable raw mode of terminal so that Ctrl-C is handled as
102 		 * SIGINT signal rather than as regular input character. */
103 		noraw();
104 	}
105 }
106 
107 void
ui_cancellation_request(void)108 ui_cancellation_request(void)
109 {
110 	if(ui_cancellation_enabled())
111 	{
112 		cancellation_state = CRS_ENABLED_REQUESTED;
113 	}
114 }
115 
116 int
ui_cancellation_requested(void)117 ui_cancellation_requested(void)
118 {
119 	return cancellation_state == CRS_ENABLED_REQUESTED
120 	    || cancellation_state == CRS_DISABLED_REQUESTED;
121 }
122 
123 void
ui_cancellation_disable(void)124 ui_cancellation_disable(void)
125 {
126 	/* TODO: consider enabling this assert:
127 	assert(!int_stack_is_empty(&cancellation_stack) && "Must push state first.");
128 	*/
129 	assert(ui_cancellation_enabled() && "Can't disable what disabled.");
130 
131 	/* The check is here for tests, which are running with uninitialized
132 	 * curses. */
133 	if(curr_stats.load_stage > 2)
134 	{
135 		/* Restore raw mode of terminal so that Ctrl-C is handled as regular input
136 		 * character rather than as SIGINT signal. */
137 		raw();
138 	}
139 
140 	cancellation_state = (cancellation_state == CRS_ENABLED_REQUESTED)
141 	                   ? CRS_DISABLED_REQUESTED
142 	                   : CRS_DISABLED;
143 }
144 
145 void
ui_cancellation_pop(void)146 ui_cancellation_pop(void)
147 {
148 	assert(!int_stack_is_empty(&cancellation_stack) && "Underflow.");
149 
150 	cancellation_request_state next = int_stack_get_top(&cancellation_stack);
151 
152 	if(ui_cancellation_enabled())
153 	{
154 		if(!is_enabled(next))
155 		{
156 			ui_cancellation_disable();
157 		}
158 	}
159 	else
160 	{
161 		if(is_enabled(next))
162 		{
163 			ui_cancellation_enable();
164 		}
165 	}
166 
167 	int_stack_pop(&cancellation_stack);
168 	cancellation_state = next;
169 }
170 
171 /* Checks whether cancellation processing is enabled.  Returns non-zero if so,
172  * otherwise zero is returned. */
173 static int
ui_cancellation_enabled(void)174 ui_cancellation_enabled(void)
175 {
176 	return is_enabled(cancellation_state);
177 }
178 
179 /* Checks if state is an enabled state.  Returns non-zero if so, otherwise zero
180  * is returned. */
181 static int
is_enabled(cancellation_request_state state)182 is_enabled(cancellation_request_state state)
183 {
184 	return state == CRS_ENABLED
185 	    || state == CRS_ENABLED_REQUESTED;
186 }
187 
188 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
189 /* vim: set cinoptions+=t0 filetype=c : */
190