1 /*
2 * Copyright (c) 2006 Markus Fisch
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Markus Fisch nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <iostream>
35 #include <fstream>
36
37 #include "Dock.hh"
38 #include "Render.hh"
39
40 using namespace std;
41 using namespace bbdock;
42
43 /**
44 * Add an Icon record to a Dock
45 *
46 * @param dock - Dock object
47 * @param record - an icon record
48 * @param delimiter - field delimiter
49 */
add(Dock & dock,char * record,const char delimiter)50 bool add( Dock &dock, char *record, const char delimiter )
51 {
52 // cut off comments, don't use strtok because it will skip leading
53 // delimiters
54 {
55 char *comment;
56
57 if( (comment = strchr( record, '#' )) )
58 *comment = 0;
59 }
60
61 enum
62 {
63 Image,
64 Command,
65 Title,
66 Idle
67 };
68
69 char *fields[Idle+1];
70
71 bzero( fields, sizeof( fields ) );
72 fields[Image] = record;
73
74 for( int n = Image; n <= Idle; )
75 {
76 char *stop;
77
78 if( !(stop = strchr( fields[n], delimiter )) )
79 break;
80
81 *(stop++) = 0;
82 fields[++n] = stop;
83 }
84
85 if( !fields[Image] ||
86 !fields[Command] )
87 return false;
88
89 unsigned int idletime = Icon::Slow;
90
91 if( fields[Idle] )
92 if( !strcasecmp( fields[Idle], "fast" ) )
93 idletime = Icon::Fast;
94 else if( !strcasecmp( fields[Idle], "fastest" ) )
95 idletime = Icon::Fastest;
96 else if( !strcasecmp( fields[Idle], "slow" ) )
97 idletime = Icon::Slow;
98 else if( !strcasecmp( fields[Idle], "lame" ) )
99 idletime = Icon::Lame;
100 else
101 {
102 unsigned int t = (unsigned int)
103 atoi( fields[Idle] );
104
105 if( t > 0 )
106 idletime = t;
107 }
108
109 return dock.add( new Icon( fields[Image], fields[Command],
110 (fields[Title] ? fields[Title] : ""), idletime ) );
111 }
112
113 /**
114 * Load icons from file
115 *
116 * @param binary - name of the currently running binary
117 * @param dock - Dock object
118 */
loadRC(char * binary,Dock & dock)119 void loadRC( char *binary, Dock &dock )
120 {
121 string rcfilename = binary;
122 string::size_type p;
123
124 if( (p = rcfilename.find_last_of( '/' )) )
125 rcfilename = rcfilename.substr( ++p );
126
127 rcfilename = "/."+rcfilename+"rc";
128 rcfilename = getenv( "HOME" )+rcfilename;
129
130 ifstream fin( rcfilename.c_str(), ios::in );
131
132 if( !fin )
133 throw "Unable to open resource configuraton file for reading";
134
135 for( string buf; getline( fin, buf ); )
136 add( dock, (char *) buf.c_str(), ':' );
137 }
138
139 /**
140 * Entry point
141 */
main(int argc,char ** argv)142 int main( int argc, char **argv )
143 {
144 Dock::Settings settings;
145 char *binary;
146
147 // process command line arguments
148 for( binary = *argv; --argc; )
149 if( **(++argv) != '-' )
150 break;
151 else
152 switch( *((*argv)+1) )
153 {
154 default:
155 cerr << "Unknown argument \"" << *argv << "\" !" << endl
156 << endl;
157 // fall through
158 case '?':
159 case 'h':
160 cout << "usage: " << binary <<
161 " [-hvdmprlcix] IMAGEFILE:COMMAND[:WINDOWTITLE;...[:IDLE]]...\n\
162 -h print this help\n\
163 -v print version\n\
164 -d WIDTHxHEIGHT outer dimensions of dock buttons\n\
165 -m TYPE define look of mark indicating a running instance,\n\
166 TYPE may be \"play\", \"dots\", \"corner\" or\n\
167 \"cross\" (default)\n\
168 -p LEFT/TOP left/top (or if negative right/bottom) padding of\n\
169 the mark in icon image\n\
170 -r ACTION determines what action is performed when a running\n\
171 icon gets right-clicked, possible ACTIONs are:\n\
172 \"nothing\" - do nothing\n\
173 \"iconfiy\" - iconify instance (default)\n\
174 \"lower\" - move instance to background\n\
175 \"close\" - shut down instance\n\
176 -l ACTION determines what action is performed when a icon\n\
177 is clicked again when the corresponding window is\n\
178 already activated, use the same ACTIONs like before,\n\
179 \"nothing\" is default\n\
180 -c match WINDOWTITLE case-sensitive (recommended)\n\
181 -i COMMAND:IMAGEFILE remotely exchange icon of this command\n\
182 -x COMMAND remotely execute icon with this command\n\
183 \n\
184 IMAGEFILE - should be path and filename of some PNG icon\n\
185 COMMAND - a script or binary to execute\n\
186 WINDOWTITLE - is a semicolon-seperated list of case-insensitive window-titles\n\
187 of corresponding application-windows. Those strings may contain\n\
188 wildcard characters (* and/or ?) to exclusively identify a\n\
189 window. By providing this list you make the icon exclusive to\n\
190 one instance of course. Clicking on already launched icons will\n\
191 raise the corresponding window instead of invoking a new\n\
192 instance.\n\
193 IDLE - idle time after triggering one icon in miliseconds, instead\n\
194 of using numbers you may also use the terms \"lame\" (~ 10 s),\n\
195 \"slow\" (~ 5 s), \"fast\" (~ 500 ms) or \"fastest\" (~ 250 ms)"
196 << endl;
197 return 0;
198 case 'v':
199 cout << "bbdock, version 0.2.9 <mf@markusfisch.de>" << endl;
200 return 0;
201 case 'd':
202 {
203 char *w;
204 char *h;
205
206 if( --argc &&
207 (w = strtok( *(++argv), "x" )) &&
208 (h = strtok( 0, "" )) )
209 {
210 settings.setSlotWidth( atoi( w ) );
211 settings.setSlotHeight( atoi( h ) );
212 }
213 else
214 cerr << "Missing or invalid dimensions !" <<
215 endl;
216 }
217 break;
218 case 'm':
219 if( !--argc )
220 cerr << "Missing mark type !" << endl;
221 else
222 {
223 char *type = *(++argv);
224
225 if( !(strcasecmp( type, "play" )) )
226 settings.setMarkType( Render::PlayMark );
227 else if( !(strcasecmp( type, "dots" )) )
228 {
229 settings.setMarkType( Render::DotsMark );
230 settings.setMarkLeft( -1 );
231 settings.setMarkTop( -1 );
232 }
233 else if( !(strcasecmp( type, "corner" )) )
234 {
235 settings.setMarkType( Render::CornerMark );
236 settings.setMarkLeft( -1 );
237 settings.setMarkTop( -1 );
238 }
239 else if( !(strcasecmp( type, "cross" )) )
240 {
241 settings.setMarkType( Render::CrossMark );
242 settings.setMarkLeft( -1 );
243 settings.setMarkTop( 0 );
244 }
245 else
246 cerr << "Unknown type '" << type << "' !" << endl;
247 }
248 break;
249 case 'p':
250 {
251 char *l;
252 char *t;
253
254 if( --argc &&
255 (l = strtok( *(++argv), "/" )) &&
256 (t = strtok( 0, "" )) )
257 {
258 settings.setMarkLeft( atoi( l ) );
259 settings.setMarkTop( atoi( t ) );
260 }
261 else
262 cerr << "Missing or invalid padding !" <<
263 endl;
264 }
265 break;
266 case 'r':
267 if( !--argc )
268 cerr << "Missing right-click action !" << endl;
269 else
270 {
271 char *action = *(++argv);
272
273 if( !strcasecmp( action, "nothing" ) )
274 settings.setRightClickAction(
275 Dock::Settings::DoNothing );
276 else if( !strcasecmp( action, "iconify" ) )
277 settings.setRightClickAction(
278 Dock::Settings::IconifyApplication );
279 else if( !strcasecmp( action, "lower" ) )
280 settings.setRightClickAction(
281 Dock::Settings::LowerApplication );
282 else if( !strcasecmp( action, "close" ) )
283 settings.setRightClickAction(
284 Dock::Settings::CloseApplication );
285 else
286 cerr << "Unknown action '" << action << "' !" <<
287 endl;
288 }
289 break;
290 case 'l':
291 if( !--argc )
292 cerr << "Missing left-click action !" << endl;
293 else
294 {
295 char *action = *(++argv);
296
297 if( !strcasecmp( action, "nothing" ) )
298 settings.setLeftClickAction(
299 Dock::Settings::DoNothing );
300 else if( !strcasecmp( action, "iconify" ) )
301 settings.setLeftClickAction(
302 Dock::Settings::IconifyApplication );
303 else if( !strcasecmp( action, "lower" ) )
304 settings.setLeftClickAction(
305 Dock::Settings::LowerApplication );
306 else if( !strcasecmp( action, "close" ) )
307 settings.setLeftClickAction(
308 Dock::Settings::CloseApplication );
309 else
310 cerr << "Unknown action '" << action << "' !" <<
311 endl;
312 }
313 break;
314 case 'c':
315 settings.setCaseSensitive( true );
316 break;
317 case 'i':
318 {
319 char *icon;
320 char *cmd;
321
322 if( --argc &&
323 (cmd = strtok( *(++argv), ":" )) &&
324 (icon = strtok( 0, "" )) )
325 {
326 Dock::changeIcon( cmd, icon );
327 return 0;
328 }
329 else
330 cerr << "Missing or invalid parameter !" <<
331 endl;
332 }
333 break;
334 case 'x':
335 {
336 if( --argc )
337 {
338 Dock::executeIcon( *(++argv) );
339 return 0;
340 }
341 else
342 cerr << "Missing or invalid parameter !" <<
343 endl;
344 }
345 break;
346 }
347
348 // detach from shell
349 switch( fork() )
350 {
351 case -1:
352 cerr << "Can not fork !" << endl;
353 return -1;
354 case 0:
355 break;
356 default:
357 return 0;
358 }
359
360 // bring up the icons
361 try
362 {
363 Dock dock( settings );
364
365 if( !argc )
366 {
367 // try to start up by configuration file
368 loadRC( binary, dock );
369 }
370 else
371 {
372 // try to start up by command line
373 for( ; argc--; argv++ )
374 if( !add( dock, *argv, ':' ) )
375 throw "Invalid argument";
376 }
377
378 dock.run();
379 }
380 catch( const char *e )
381 {
382 cerr << e << endl;
383 }
384 catch( ... )
385 {
386 cerr << "Unknown error" << endl;
387 }
388
389 return 0;
390 }
391