i3
Main Page
Data Structures
Files
File List
Globals
src
scratchpad.c
Go to the documentation of this file.
1
#undef I3__FILE__
2
#define I3__FILE__ "scratchpad.c"
3
/*
4
* vim:ts=4:sw=4:expandtab
5
*
6
* i3 - an improved dynamic tiling window manager
7
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
8
*
9
* scratchpad.c: Moving windows to the scratchpad and making them visible again.
10
*
11
*/
12
#include "
all.h
"
13
14
/*
15
* Moves the specified window to the __i3_scratch workspace, making it floating
16
* and setting the appropriate scratchpad_state.
17
*
18
* Gets called upon the command 'move scratchpad'.
19
*
20
*/
21
void
scratchpad_move
(
Con
*con) {
22
if
(con->
type
== CT_WORKSPACE) {
23
LOG
(
"'move scratchpad' used on a workspace \"%s\". Calling it "
24
"recursively on all windows on this workspace.\n"
, con->
name
);
25
Con
*current;
26
current =
TAILQ_FIRST
(&(con->focus_head));
27
while
(current) {
28
Con
*next =
TAILQ_NEXT
(current,
focused
);
29
scratchpad_move
(current);
30
current = next;
31
}
32
return
;
33
}
34
DLOG
(
"should move con %p to __i3_scratch\n"
, con);
35
36
Con
*__i3_scratch =
workspace_get
(
"__i3_scratch"
, NULL);
37
if
(
con_get_workspace
(con) == __i3_scratch) {
38
DLOG
(
"This window is already on __i3_scratch.\n"
);
39
return
;
40
}
41
42
/* 1: Ensure the window is floating. From now on, we deal with the
43
* CT_FLOATING_CON. We use automatic == false because the user made the
44
* choice that this window should be a scratchpad (and floating). */
45
floating_enable
(con,
false
);
46
con = con->
parent
;
47
48
/* 2: Send the window to the __i3_scratch workspace, mainting its
49
* coordinates and not warping the pointer. */
50
Con
*focus_next =
con_next_focused
(con);
51
con_move_to_workspace
(con, __i3_scratch,
true
,
true
);
52
53
/* 3: If this is the first time this window is used as a scratchpad, we set
54
* the scratchpad_state to SCRATCHPAD_FRESH. The window will then be
55
* adjusted in size according to what the user specifies. */
56
if
(con->
scratchpad_state
== SCRATCHPAD_NONE) {
57
DLOG
(
"This window was never used as a scratchpad before.\n"
);
58
con->
scratchpad_state
= SCRATCHPAD_FRESH;
59
}
60
61
/* 4: Fix focus. Normally, when moving a window to a different output, the
62
* destination output gets focused. In this case, we don’t want that. */
63
if
(
con_get_workspace
(focus_next) ==
con_get_workspace
(
focused
))
64
con_focus
(focus_next);
65
}
66
67
/*
68
* Either shows the top-most scratchpad window (con == NULL) or shows the
69
* specified con (if it is scratchpad window).
70
*
71
* When called with con == NULL and the currently focused window is a
72
* scratchpad window, this serves as a shortcut to hide it again (so the user
73
* can press the same key to quickly look something up).
74
*
75
*/
76
void
scratchpad_show
(
Con
*con) {
77
DLOG
(
"should show scratchpad window %p\n"
, con);
78
Con
*__i3_scratch =
workspace_get
(
"__i3_scratch"
, NULL);
79
Con
*floating;
80
81
/* If the current con or any of its parents are in fullscreen mode, we
82
* first need to disable it before showing the scratchpad con. */
83
Con
*fs =
focused
;
84
while
(fs && fs->
fullscreen_mode
== CF_NONE)
85
fs = fs->
parent
;
86
87
if
(fs->
type
!= CT_WORKSPACE) {
88
con_toggle_fullscreen
(
focused
, CF_OUTPUT);
89
}
90
91
/* If this was 'scratchpad show' without criteria, we check if the
92
* currently focused window is a scratchpad window and should be hidden
93
* again. */
94
if
(!con &&
95
(floating =
con_inside_floating
(
focused
)) &&
96
floating->
scratchpad_state
!= SCRATCHPAD_NONE) {
97
DLOG
(
"Focused window is a scratchpad window, hiding it.\n"
);
98
scratchpad_move
(
focused
);
99
return
;
100
}
101
102
/* If this was 'scratchpad show' with criteria, we check if it matches a
103
* currently visible scratchpad window and hide it. */
104
Con
*active =
con_get_workspace
(
focused
);
105
Con
*current =
con_get_workspace
(con);
106
if
(con &&
107
(floating =
con_inside_floating
(con)) &&
108
floating->
scratchpad_state
!= SCRATCHPAD_NONE &&
109
current != __i3_scratch) {
110
/* If scratchpad window is on the active workspace, then we should hide
111
* it, otherwise we should move it to the active workspace. */
112
if
(current == active) {
113
DLOG
(
"Window is a scratchpad window, hiding it.\n"
);
114
scratchpad_move
(con);
115
return
;
116
}
117
}
118
119
if
(con == NULL) {
120
/* Use the container on __i3_scratch which is highest in the focus
121
* stack. When moving windows to __i3_scratch, they get inserted at the
122
* bottom of the stack. */
123
con =
TAILQ_FIRST
(&(__i3_scratch->floating_head));
124
125
if
(!con) {
126
LOG
(
"You don't have any scratchpad windows yet.\n"
);
127
LOG
(
"Use 'move scratchpad' to move a window to the scratchpad.\n"
);
128
return
;
129
}
130
}
131
132
/* 1: Move the window from __i3_scratch to the current workspace. */
133
con_move_to_workspace
(con, active,
true
,
false
);
134
135
/* 2: Adjust the size if this window was not adjusted yet. */
136
if
(con->
scratchpad_state
== SCRATCHPAD_FRESH) {
137
DLOG
(
"Adjusting size of this window.\n"
);
138
Con
*
output
=
con_get_output
(con);
139
con->
rect
.
width
= output->
rect
.
width
* 0.5;
140
con->
rect
.
height
= output->
rect
.
height
* 0.75;
141
con->
rect
.
x
= output->
rect
.
x
+
142
((output->
rect
.
width
/ 2.0) - (con->
rect
.
width
/ 2.0));
143
con->
rect
.
y
= output->
rect
.
y
+
144
((output->
rect
.
height
/ 2.0) - (con->
rect
.
height
/ 2.0));
145
con->
scratchpad_state
= SCRATCHPAD_CHANGED;
146
}
147
148
/* Activate active workspace if window is from another workspace to ensure
149
* proper focus. */
150
if
(current != active) {
151
workspace_show
(active);
152
}
153
154
con_focus
(
con_descend_focused
(con));
155
}
156
157
/*
158
* Greatest common divisor, implemented only for the least common multiple
159
* below.
160
*
161
*/
162
static
int
_gcd
(
const
int
m,
const
int
n) {
163
if
(n == 0)
164
return
m;
165
return
_gcd
(n, (m % n));
166
}
167
168
/*
169
* Least common multiple. We use it to determine the (ideally not too large)
170
* resolution for the __i3 pseudo-output on which the scratchpad is on (see
171
* below). We could just multiply the resolutions, but for some pathetic cases
172
* (many outputs), using the LCM will achieve better results.
173
*
174
* Man, when you were learning about these two algorithms for the first time,
175
* did you think you’d ever need them in a real-world software project of
176
* yours? I certainly didn’t until now. :-D
177
*
178
*/
179
static
int
_lcm
(
const
int
m,
const
int
n) {
180
const
int
o =
_gcd
(m, n);
181
return
((m * n) / o);
182
}
183
184
/*
185
* When starting i3 initially (and after each change to the connected outputs),
186
* this function fixes the resolution of the __i3 pseudo-output. When that
187
* resolution is not set to a function which shares a common divisor with every
188
* active output’s resolution, floating point calculation errors will lead to
189
* the scratchpad window moving when shown repeatedly.
190
*
191
*/
192
void
scratchpad_fix_resolution
(
void
) {
193
Con
*__i3_scratch =
workspace_get
(
"__i3_scratch"
, NULL);
194
Con
*__i3_output =
con_get_output
(__i3_scratch);
195
DLOG
(
"Current resolution: (%d, %d) %d x %d\n"
,
196
__i3_output->
rect
.
x
, __i3_output->
rect
.
y
,
197
__i3_output->
rect
.
width
, __i3_output->
rect
.
height
);
198
Con
*
output
;
199
int
new_width = -1,
200
new_height = -1;
201
TAILQ_FOREACH
(output, &(
croot
->nodes_head), nodes) {
202
if
(output == __i3_output)
203
continue
;
204
DLOG
(
"output %s's resolution: (%d, %d) %d x %d\n"
,
205
output->
name
, output->
rect
.
x
, output->
rect
.
y
,
206
output->
rect
.
width
, output->
rect
.
height
);
207
if
(new_width == -1) {
208
new_width = output->
rect
.
width
;
209
new_height = output->
rect
.
height
;
210
}
else
{
211
new_width =
_lcm
(new_width, output->
rect
.
width
);
212
new_height =
_lcm
(new_height, output->
rect
.
height
);
213
}
214
}
215
DLOG
(
"new width = %d, new height = %d\n"
,
216
new_width, new_height);
217
__i3_output->
rect
.
width
= new_width;
218
__i3_output->
rect
.
height
= new_height;
219
}
Generated by
1.8.1.2