├── .gitignore ├── .uncrustify.cfg ├── CONTRIBUTING.asciidoc ├── LICENSE ├── README.asciidoc ├── docs ├── .gitignore ├── CONTRIBUTING.asciidoc ├── INSTALL.asciidoc ├── candybar.1.asciidoc └── candybar.5.asciidoc ├── src ├── candybar.c ├── candybar.h ├── config.json ├── util │ ├── config.c │ ├── config.h │ ├── copy_prop.c │ ├── copy_prop.h │ ├── curl.c │ ├── curl.h │ ├── dbus_helpers.c │ ├── dbus_helpers.h │ ├── gdk_helpers.c │ ├── gdk_helpers.h │ ├── log.c │ ├── log.h │ ├── process.c │ └── process.h ├── widgets.c ├── widgets.h └── widgets │ ├── battery.c │ ├── battery.h │ ├── datetime.c │ ├── datetime.h │ ├── desktops.c │ ├── desktops.h │ ├── desktops_i3.c │ ├── desktops_i3.h │ ├── email_imap.c │ ├── email_imap.h │ ├── external_ip.c │ ├── external_ip.h │ ├── magick_background.c │ ├── magick_background.h │ ├── notifications.c │ ├── notifications.h │ ├── now_playing_mpd.c │ ├── now_playing_mpd.h │ ├── now_playing_mpris.c │ ├── now_playing_mpris.h │ ├── volume.c │ ├── volume.h │ ├── weather.c │ ├── weather.h │ ├── window_title.c │ └── window_title.h ├── waf └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .lock-waf* 3 | .waf* 4 | -------------------------------------------------------------------------------- /.uncrustify.cfg: -------------------------------------------------------------------------------- 1 | # Uncrustify 0.60 2 | 3 | # 4 | # General options 5 | # 6 | 7 | # The type of line endings 8 | newlines = auto # auto/lf/crlf/cr 9 | 10 | # The original size of tabs in the input 11 | input_tab_size = 8 # number 12 | 13 | # The size of tabs in the output (only used if align_with_tabs=true) 14 | output_tab_size = 8 # number 15 | 16 | # The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn) 17 | string_escape_char = 92 # number 18 | 19 | # Alternate string escape char for Pawn. Only works right before the quote char. 20 | string_escape_char2 = 0 # number 21 | 22 | # Allow interpreting '>=' and '>>=' as part of a template in 'void f(list>=val);'. 23 | # If true (default), 'assert(x<0 && y>=3)' will be broken. 24 | # Improvements to template detection may make this option obsolete. 25 | tok_split_gte = false # false/true 26 | 27 | # Control what to do with the UTF-8 BOM (recommend 'remove') 28 | utf8_bom = remove # ignore/add/remove/force 29 | 30 | # If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 31 | utf8_byte = false # false/true 32 | 33 | # Force the output encoding to UTF-8 34 | utf8_force = true # false/true 35 | 36 | # 37 | # Indenting 38 | # 39 | 40 | # The number of columns to indent per level. 41 | # Usually 2, 3, 4, or 8. 42 | indent_columns = 8 # number 43 | 44 | # The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. 45 | # For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level 46 | indent_continue = 0 # number 47 | 48 | # How to use tabs when indenting code 49 | # 0=spaces only 50 | # 1=indent with tabs to brace level, align with spaces 51 | # 2=indent and align with tabs, using spaces when not on a tabstop 52 | indent_with_tabs = 1 # number 53 | 54 | # Comments that are not a brace level are indented with tabs on a tabstop. 55 | # Requires indent_with_tabs=2. If false, will use spaces. 56 | indent_cmt_with_tabs = false # false/true 57 | 58 | # Whether to indent strings broken by '\' so that they line up 59 | indent_align_string = true # false/true 60 | 61 | # The number of spaces to indent multi-line XML strings. 62 | # Requires indent_align_string=True 63 | indent_xml_string = 0 # number 64 | 65 | # Spaces to indent '{' from level 66 | indent_brace = 0 # number 67 | 68 | # Whether braces are indented to the body level 69 | indent_braces = false # false/true 70 | 71 | # Disabled indenting function braces if indent_braces is true 72 | indent_braces_no_func = false # false/true 73 | 74 | # Disabled indenting class braces if indent_braces is true 75 | indent_braces_no_class = false # false/true 76 | 77 | # Disabled indenting struct braces if indent_braces is true 78 | indent_braces_no_struct = false # false/true 79 | 80 | # Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. 81 | indent_brace_parent = false # false/true 82 | 83 | # Whether the 'namespace' body is indented 84 | indent_namespace = false # false/true 85 | 86 | # The number of spaces to indent a namespace block 87 | indent_namespace_level = 0 # number 88 | 89 | # If the body of the namespace is longer than this number, it won't be indented. 90 | # Requires indent_namespace=true. Default=0 (no limit) 91 | indent_namespace_limit = 0 # number 92 | 93 | # Whether the 'extern "C"' body is indented 94 | indent_extern = false # false/true 95 | 96 | # Whether the 'class' body is indented 97 | indent_class = false # false/true 98 | 99 | # Whether to indent the stuff after a leading class colon 100 | indent_class_colon = false # false/true 101 | 102 | # Virtual indent from the ':' for member initializers. Default is 2 103 | indent_ctor_init_leading = 2 # number 104 | 105 | # Additional indenting for constructor initializer list 106 | indent_ctor_init = 0 # number 107 | 108 | # False=treat 'else\nif' as 'else if' for indenting purposes 109 | # True=indent the 'if' one level 110 | indent_else_if = false # false/true 111 | 112 | # Amount to indent variable declarations after a open brace. neg=relative, pos=absolute 113 | indent_var_def_blk = 0 # number 114 | 115 | # Indent continued variable declarations instead of aligning. 116 | indent_var_def_cont = false # false/true 117 | 118 | # True: force indentation of function definition to start in column 1 119 | # False: use the default behavior 120 | indent_func_def_force_col1 = false # false/true 121 | 122 | # True: indent continued function call parameters one indent level 123 | # False: align parameters under the open paren 124 | indent_func_call_param = false # false/true 125 | 126 | # Same as indent_func_call_param, but for function defs 127 | indent_func_def_param = false # false/true 128 | 129 | # Same as indent_func_call_param, but for function protos 130 | indent_func_proto_param = false # false/true 131 | 132 | # Same as indent_func_call_param, but for class declarations 133 | indent_func_class_param = false # false/true 134 | 135 | # Same as indent_func_call_param, but for class variable constructors 136 | indent_func_ctor_var_param = false # false/true 137 | 138 | # Same as indent_func_call_param, but for templates 139 | indent_template_param = false # false/true 140 | 141 | # Double the indent for indent_func_xxx_param options 142 | indent_func_param_double = false # false/true 143 | 144 | # Indentation column for standalone 'const' function decl/proto qualifier 145 | indent_func_const = 0 # number 146 | 147 | # Indentation column for standalone 'throw' function decl/proto qualifier 148 | indent_func_throw = 0 # number 149 | 150 | # The number of spaces to indent a continued '->' or '.' 151 | # Usually set to 0, 1, or indent_columns. 152 | indent_member = 0 # number 153 | 154 | # Spaces to indent single line ('//') comments on lines before code 155 | indent_sing_line_comments = 0 # number 156 | 157 | # If set, will indent trailing single line ('//') comments relative 158 | # to the code instead of trying to keep the same absolute column 159 | indent_relative_single_line_comments = false # false/true 160 | 161 | # Spaces to indent 'case' from 'switch' 162 | # Usually 0 or indent_columns. 163 | indent_switch_case = 0 # number 164 | 165 | # Spaces to shift the 'case' line, without affecting any other lines 166 | # Usually 0. 167 | indent_case_shift = 0 # number 168 | 169 | # Spaces to indent '{' from 'case'. 170 | # By default, the brace will appear under the 'c' in case. 171 | # Usually set to 0 or indent_columns. 172 | indent_case_brace = 0 # number 173 | 174 | # Whether to indent comments found in first column 175 | indent_col1_comment = false # false/true 176 | 177 | # How to indent goto labels 178 | # >0 : absolute column where 1 is the leftmost column 179 | # <=0 : subtract from brace indent 180 | indent_label = 1 # number 181 | 182 | # Same as indent_label, but for access specifiers that are followed by a colon 183 | indent_access_spec = 1 # number 184 | 185 | # Indent the code after an access specifier by one level. 186 | # If set, this option forces 'indent_access_spec=0' 187 | indent_access_spec_body = false # false/true 188 | 189 | # If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) 190 | indent_paren_nl = false # false/true 191 | 192 | # Controls the indent of a close paren after a newline. 193 | # 0: Indent to body level 194 | # 1: Align under the open paren 195 | # 2: Indent to the brace level 196 | indent_paren_close = 0 # number 197 | 198 | # Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren 199 | indent_comma_paren = false # false/true 200 | 201 | # Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren 202 | indent_bool_paren = false # false/true 203 | 204 | # If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones 205 | indent_first_bool_expr = false # false/true 206 | 207 | # If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) 208 | indent_square_nl = false # false/true 209 | 210 | # Don't change the relative indent of ESQL/C 'EXEC SQL' bodies 211 | indent_preserve_sql = false # false/true 212 | 213 | # Align continued statements at the '='. Default=True 214 | # If FALSE or the '=' is followed by a newline, the next line is indent one tab. 215 | indent_align_assign = true # false/true 216 | 217 | # Indent OC blocks at brace level instead of usual rules. 218 | indent_oc_block = false # false/true 219 | 220 | # Indent OC blocks in a message relative to the parameter name. 221 | # 0=use indent_oc_block rules, 1+=spaces to indent 222 | indent_oc_block_msg = 0 # number 223 | 224 | # Minimum indent for subsequent parameters 225 | indent_oc_msg_colon = 0 # number 226 | 227 | # 228 | # Spacing options 229 | # 230 | 231 | # Add or remove space around arithmetic operator '+', '-', '/', '*', etc 232 | sp_arith = force # ignore/add/remove/force 233 | 234 | # Add or remove space around assignment operator '=', '+=', etc 235 | sp_assign = force # ignore/add/remove/force 236 | 237 | # Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign 238 | sp_cpp_lambda_assign = ignore # ignore/add/remove/force 239 | 240 | # Add or remove space after the capture specification in C++11 lambda. 241 | sp_cpp_lambda_paren = ignore # ignore/add/remove/force 242 | 243 | # Add or remove space around assignment operator '=' in a prototype 244 | sp_assign_default = ignore # ignore/add/remove/force 245 | 246 | # Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. 247 | sp_before_assign = ignore # ignore/add/remove/force 248 | 249 | # Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. 250 | sp_after_assign = ignore # ignore/add/remove/force 251 | 252 | # Add or remove space around assignment '=' in enum 253 | sp_enum_assign = ignore # ignore/add/remove/force 254 | 255 | # Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. 256 | sp_enum_before_assign = ignore # ignore/add/remove/force 257 | 258 | # Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. 259 | sp_enum_after_assign = ignore # ignore/add/remove/force 260 | 261 | # Add or remove space around preprocessor '##' concatenation operator. Default=Add 262 | sp_pp_concat = add # ignore/add/remove/force 263 | 264 | # Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. 265 | sp_pp_stringify = ignore # ignore/add/remove/force 266 | 267 | # Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. 268 | sp_before_pp_stringify = ignore # ignore/add/remove/force 269 | 270 | # Add or remove space around boolean operators '&&' and '||' 271 | sp_bool = force # ignore/add/remove/force 272 | 273 | # Add or remove space around compare operator '<', '>', '==', etc 274 | sp_compare = force # ignore/add/remove/force 275 | 276 | # Add or remove space inside '(' and ')' 277 | sp_inside_paren = ignore # ignore/add/remove/force 278 | 279 | # Add or remove space between nested parens: '((' vs ') )' 280 | sp_paren_paren = ignore # ignore/add/remove/force 281 | 282 | # Add or remove space between back-to-back parens: ')(' vs ') (' 283 | sp_cparen_oparen = ignore # ignore/add/remove/force 284 | 285 | # Whether to balance spaces inside nested parens 286 | sp_balance_nested_parens = false # false/true 287 | 288 | # Add or remove space between ')' and '{' 289 | sp_paren_brace = force # ignore/add/remove/force 290 | 291 | # Add or remove space before pointer star '*' 292 | sp_before_ptr_star = force # ignore/add/remove/force 293 | 294 | # Add or remove space before pointer star '*' that isn't followed by a variable name 295 | # If set to 'ignore', sp_before_ptr_star is used instead. 296 | sp_before_unnamed_ptr_star = remove # ignore/add/remove/force 297 | 298 | # Add or remove space between pointer stars '*' 299 | sp_between_ptr_star = remove # ignore/add/remove/force 300 | 301 | # Add or remove space after pointer star '*', if followed by a word. 302 | sp_after_ptr_star = remove # ignore/add/remove/force 303 | 304 | # Add or remove space after a pointer star '*', if followed by a func proto/def. 305 | sp_after_ptr_star_func = force # ignore/add/remove/force 306 | 307 | # Add or remove space after a pointer star '*', if followed by an open paren (function types). 308 | sp_ptr_star_paren = remove # ignore/add/remove/force 309 | 310 | # Add or remove space before a pointer star '*', if followed by a func proto/def. 311 | sp_before_ptr_star_func = remove # ignore/add/remove/force 312 | 313 | # Add or remove space before a reference sign '&' 314 | sp_before_byref = remove # ignore/add/remove/force 315 | 316 | # Add or remove space before a reference sign '&' that isn't followed by a variable name 317 | # If set to 'ignore', sp_before_byref is used instead. 318 | sp_before_unnamed_byref = ignore # ignore/add/remove/force 319 | 320 | # Add or remove space after reference sign '&', if followed by a word. 321 | sp_after_byref = remove # ignore/add/remove/force 322 | 323 | # Add or remove space after a reference sign '&', if followed by a func proto/def. 324 | sp_after_byref_func = remove # ignore/add/remove/force 325 | 326 | # Add or remove space before a reference sign '&', if followed by a func proto/def. 327 | sp_before_byref_func = force # ignore/add/remove/force 328 | 329 | # Add or remove space between type and word. Default=Force 330 | sp_after_type = force # ignore/add/remove/force 331 | 332 | # Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. 333 | sp_before_template_paren = ignore # ignore/add/remove/force 334 | 335 | # Add or remove space in 'template <' vs 'template<'. 336 | # If set to ignore, sp_before_angle is used. 337 | sp_template_angle = ignore # ignore/add/remove/force 338 | 339 | # Add or remove space before '<>' 340 | sp_before_angle = ignore # ignore/add/remove/force 341 | 342 | # Add or remove space inside '<' and '>' 343 | sp_inside_angle = ignore # ignore/add/remove/force 344 | 345 | # Add or remove space after '<>' 346 | sp_after_angle = ignore # ignore/add/remove/force 347 | 348 | # Add or remove space between '<>' and '(' as found in 'new List();' 349 | sp_angle_paren = ignore # ignore/add/remove/force 350 | 351 | # Add or remove space between '<>' and a word as in 'List m;' 352 | sp_angle_word = ignore # ignore/add/remove/force 353 | 354 | # Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add 355 | sp_angle_shift = add # ignore/add/remove/force 356 | 357 | # Permit removal of the space between '>>' in 'foo >' (C++11 only). Default=False 358 | # sp_angle_shift cannot remove the space without this option. 359 | sp_permit_cpp11_shift = false # false/true 360 | 361 | # Add or remove space before '(' of 'if', 'for', 'switch', and 'while' 362 | sp_before_sparen = force # ignore/add/remove/force 363 | 364 | # Add or remove space inside if-condition '(' and ')' 365 | sp_inside_sparen = remove # ignore/add/remove/force 366 | 367 | # Add or remove space before if-condition ')'. Overrides sp_inside_sparen. 368 | sp_inside_sparen_close = ignore # ignore/add/remove/force 369 | 370 | # Add or remove space before if-condition '('. Overrides sp_inside_sparen. 371 | sp_inside_sparen_open = ignore # ignore/add/remove/force 372 | 373 | # Add or remove space after ')' of 'if', 'for', 'switch', and 'while' 374 | sp_after_sparen = ignore # ignore/add/remove/force 375 | 376 | # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' 377 | sp_sparen_brace = force # ignore/add/remove/force 378 | 379 | # Add or remove space between 'invariant' and '(' in the D language. 380 | sp_invariant_paren = ignore # ignore/add/remove/force 381 | 382 | # Add or remove space after the ')' in 'invariant (C) c' in the D language. 383 | sp_after_invariant_paren = ignore # ignore/add/remove/force 384 | 385 | # Add or remove space before empty statement ';' on 'if', 'for' and 'while' 386 | sp_special_semi = ignore # ignore/add/remove/force 387 | 388 | # Add or remove space before ';'. Default=Remove 389 | sp_before_semi = remove # ignore/add/remove/force 390 | 391 | # Add or remove space before ';' in non-empty 'for' statements 392 | sp_before_semi_for = remove # ignore/add/remove/force 393 | 394 | # Add or remove space before a semicolon of an empty part of a for statement. 395 | sp_before_semi_for_empty = ignore # ignore/add/remove/force 396 | 397 | # Add or remove space after ';', except when followed by a comment. Default=Add 398 | sp_after_semi = add # ignore/add/remove/force 399 | 400 | # Add or remove space after ';' in non-empty 'for' statements. Default=Force 401 | sp_after_semi_for = force # ignore/add/remove/force 402 | 403 | # Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; ). 404 | sp_after_semi_for_empty = remove # ignore/add/remove/force 405 | 406 | # Add or remove space before '[' (except '[]') 407 | sp_before_square = remove # ignore/add/remove/force 408 | 409 | # Add or remove space before '[]' 410 | sp_before_squares = remove # ignore/add/remove/force 411 | 412 | # Add or remove space inside a non-empty '[' and ']' 413 | sp_inside_square = remove # ignore/add/remove/force 414 | 415 | # Add or remove space after ',' 416 | sp_after_comma = force # ignore/add/remove/force 417 | 418 | # Add or remove space before ',' 419 | sp_before_comma = remove # ignore/add/remove/force 420 | 421 | # Add or remove space between an open paren and comma: '(,' vs '( ,' 422 | sp_paren_comma = force # ignore/add/remove/force 423 | 424 | # Add or remove space before the variadic '...' when preceded by a non-punctuator 425 | sp_before_ellipsis = ignore # ignore/add/remove/force 426 | 427 | # Add or remove space after class ':' 428 | sp_after_class_colon = ignore # ignore/add/remove/force 429 | 430 | # Add or remove space before class ':' 431 | sp_before_class_colon = ignore # ignore/add/remove/force 432 | 433 | # Add or remove space before case ':'. Default=Remove 434 | sp_before_case_colon = remove # ignore/add/remove/force 435 | 436 | # Add or remove space between 'operator' and operator sign 437 | sp_after_operator = ignore # ignore/add/remove/force 438 | 439 | # Add or remove space between the operator symbol and the open paren, as in 'operator ++(' 440 | sp_after_operator_sym = remove # ignore/add/remove/force 441 | 442 | # Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' 443 | sp_after_cast = remove # ignore/add/remove/force 444 | 445 | # Add or remove spaces inside cast parens 446 | sp_inside_paren_cast = remove # ignore/add/remove/force 447 | 448 | # Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)' 449 | sp_cpp_cast_paren = ignore # ignore/add/remove/force 450 | 451 | # Add or remove space between 'sizeof' and '(' 452 | sp_sizeof_paren = remove # ignore/add/remove/force 453 | 454 | # Add or remove space after the tag keyword (Pawn) 455 | sp_after_tag = ignore # ignore/add/remove/force 456 | 457 | # Add or remove space inside enum '{' and '}' 458 | sp_inside_braces_enum = force # ignore/add/remove/force 459 | 460 | # Add or remove space inside struct/union '{' and '}' 461 | sp_inside_braces_struct = force # ignore/add/remove/force 462 | 463 | # Add or remove space inside '{' and '}' 464 | sp_inside_braces = force # ignore/add/remove/force 465 | 466 | # Add or remove space inside '{}' 467 | sp_inside_braces_empty = force # ignore/add/remove/force 468 | 469 | # Add or remove space between return type and function name 470 | # A minimum of 1 is forced except for pointer return types. 471 | sp_type_func = remove # ignore/add/remove/force 472 | 473 | # Add or remove space between function name and '(' on function declaration 474 | sp_func_proto_paren = force # ignore/add/remove/force 475 | 476 | # Add or remove space between function name and '(' on function definition 477 | sp_func_def_paren = force # ignore/add/remove/force 478 | 479 | # Add or remove space inside empty function '()' 480 | sp_inside_fparens = remove # ignore/add/remove/force 481 | 482 | # Add or remove space inside function '(' and ')' 483 | sp_inside_fparen = remove # ignore/add/remove/force 484 | 485 | # Add or remove space inside the first parens in the function type: 'void (*x)(...)' 486 | sp_inside_tparen = remove # ignore/add/remove/force 487 | 488 | # Add or remove between the parens in the function type: 'void (*x)(...)' 489 | sp_after_tparen_close = remove # ignore/add/remove/force 490 | 491 | # Add or remove space between ']' and '(' when part of a function call. 492 | sp_square_fparen = remove # ignore/add/remove/force 493 | 494 | # Add or remove space between ')' and '{' of function 495 | sp_fparen_brace = force # ignore/add/remove/force 496 | 497 | # Add or remove space between function name and '(' on function calls 498 | sp_func_call_paren = remove # ignore/add/remove/force 499 | 500 | # Add or remove space between function name and '()' on function calls without parameters. 501 | # If set to 'ignore' (the default), sp_func_call_paren is used. 502 | sp_func_call_paren_empty = ignore # ignore/add/remove/force 503 | 504 | # Add or remove space between the user function name and '(' on function calls 505 | # You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. 506 | sp_func_call_user_paren = ignore # ignore/add/remove/force 507 | 508 | # Add or remove space between a constructor/destructor and the open paren 509 | sp_func_class_paren = ignore # ignore/add/remove/force 510 | 511 | # Add or remove space between 'return' and '(' 512 | sp_return_paren = force # ignore/add/remove/force 513 | 514 | # Add or remove space between '__attribute__' and '(' 515 | sp_attribute_paren = ignore # ignore/add/remove/force 516 | 517 | # Add or remove space between 'defined' and '(' in '#if defined (FOO)' 518 | sp_defined_paren = force # ignore/add/remove/force 519 | 520 | # Add or remove space between 'throw' and '(' in 'throw (something)' 521 | sp_throw_paren = ignore # ignore/add/remove/force 522 | 523 | # Add or remove space between 'throw' and anything other than '(' as in '@throw [...];' 524 | sp_after_throw = ignore # ignore/add/remove/force 525 | 526 | # Add or remove space between 'catch' and '(' in 'catch (something) { }' 527 | # If set to ignore, sp_before_sparen is used. 528 | sp_catch_paren = ignore # ignore/add/remove/force 529 | 530 | # Add or remove space between 'version' and '(' in 'version (something) { }' (D language) 531 | # If set to ignore, sp_before_sparen is used. 532 | sp_version_paren = ignore # ignore/add/remove/force 533 | 534 | # Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) 535 | # If set to ignore, sp_before_sparen is used. 536 | sp_scope_paren = ignore # ignore/add/remove/force 537 | 538 | # Add or remove space between macro and value 539 | sp_macro = ignore # ignore/add/remove/force 540 | 541 | # Add or remove space between macro function ')' and value 542 | sp_macro_func = ignore # ignore/add/remove/force 543 | 544 | # Add or remove space between 'else' and '{' if on the same line 545 | sp_else_brace = force # ignore/add/remove/force 546 | 547 | # Add or remove space between '}' and 'else' if on the same line 548 | sp_brace_else = force # ignore/add/remove/force 549 | 550 | # Add or remove space between '}' and the name of a typedef on the same line 551 | sp_brace_typedef = force # ignore/add/remove/force 552 | 553 | # Add or remove space between 'catch' and '{' if on the same line 554 | sp_catch_brace = ignore # ignore/add/remove/force 555 | 556 | # Add or remove space between '}' and 'catch' if on the same line 557 | sp_brace_catch = ignore # ignore/add/remove/force 558 | 559 | # Add or remove space between 'finally' and '{' if on the same line 560 | sp_finally_brace = ignore # ignore/add/remove/force 561 | 562 | # Add or remove space between '}' and 'finally' if on the same line 563 | sp_brace_finally = ignore # ignore/add/remove/force 564 | 565 | # Add or remove space between 'try' and '{' if on the same line 566 | sp_try_brace = ignore # ignore/add/remove/force 567 | 568 | # Add or remove space between get/set and '{' if on the same line 569 | sp_getset_brace = ignore # ignore/add/remove/force 570 | 571 | # Add or remove space between a variable and '{' for C++ uniform initialization 572 | sp_word_brace = add # ignore/add/remove/force 573 | 574 | # Add or remove space between a variable and '{' for a namespace 575 | sp_word_brace_ns = add # ignore/add/remove/force 576 | 577 | # Add or remove space before the '::' operator 578 | sp_before_dc = ignore # ignore/add/remove/force 579 | 580 | # Add or remove space after the '::' operator 581 | sp_after_dc = ignore # ignore/add/remove/force 582 | 583 | # Add or remove around the D named array initializer ':' operator 584 | sp_d_array_colon = ignore # ignore/add/remove/force 585 | 586 | # Add or remove space after the '!' (not) operator. Default=Remove 587 | sp_not = remove # ignore/add/remove/force 588 | 589 | # Add or remove space after the '~' (invert) operator. Default=Remove 590 | sp_inv = remove # ignore/add/remove/force 591 | 592 | # Add or remove space after the '&' (address-of) operator. Default=Remove 593 | # This does not affect the spacing after a '&' that is part of a type. 594 | sp_addr = remove # ignore/add/remove/force 595 | 596 | # Add or remove space around the '.' or '->' operators. Default=Remove 597 | sp_member = remove # ignore/add/remove/force 598 | 599 | # Add or remove space after the '*' (dereference) operator. Default=Remove 600 | # This does not affect the spacing after a '*' that is part of a type. 601 | sp_deref = remove # ignore/add/remove/force 602 | 603 | # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove 604 | sp_sign = remove # ignore/add/remove/force 605 | 606 | # Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove 607 | sp_incdec = remove # ignore/add/remove/force 608 | 609 | # Add or remove space before a backslash-newline at the end of a line. Default=Add 610 | sp_before_nl_cont = add # ignore/add/remove/force 611 | 612 | # Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' 613 | sp_after_oc_scope = ignore # ignore/add/remove/force 614 | 615 | # Add or remove space after the colon in message specs 616 | # '-(int) f:(int) x;' vs '-(int) f: (int) x;' 617 | sp_after_oc_colon = ignore # ignore/add/remove/force 618 | 619 | # Add or remove space before the colon in message specs 620 | # '-(int) f: (int) x;' vs '-(int) f : (int) x;' 621 | sp_before_oc_colon = ignore # ignore/add/remove/force 622 | 623 | # Add or remove space after the colon in immutable dictionary expression 624 | # 'NSDictionary *test = @{@"foo" :@"bar"};' 625 | sp_after_oc_dict_colon = ignore # ignore/add/remove/force 626 | 627 | # Add or remove space before the colon in immutable dictionary expression 628 | # 'NSDictionary *test = @{@"foo" :@"bar"};' 629 | sp_before_oc_dict_colon = ignore # ignore/add/remove/force 630 | 631 | # Add or remove space after the colon in message specs 632 | # '[object setValue:1];' vs '[object setValue: 1];' 633 | sp_after_send_oc_colon = ignore # ignore/add/remove/force 634 | 635 | # Add or remove space before the colon in message specs 636 | # '[object setValue:1];' vs '[object setValue :1];' 637 | sp_before_send_oc_colon = ignore # ignore/add/remove/force 638 | 639 | # Add or remove space after the (type) in message specs 640 | # '-(int)f: (int) x;' vs '-(int)f: (int)x;' 641 | sp_after_oc_type = ignore # ignore/add/remove/force 642 | 643 | # Add or remove space after the first (type) in message specs 644 | # '-(int) f:(int)x;' vs '-(int)f:(int)x;' 645 | sp_after_oc_return_type = ignore # ignore/add/remove/force 646 | 647 | # Add or remove space between '@selector' and '(' 648 | # '@selector(msgName)' vs '@selector (msgName)' 649 | # Also applies to @protocol() constructs 650 | sp_after_oc_at_sel = ignore # ignore/add/remove/force 651 | 652 | # Add or remove space between '@selector(x)' and the following word 653 | # '@selector(foo) a:' vs '@selector(foo)a:' 654 | sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force 655 | 656 | # Add or remove space inside '@selector' parens 657 | # '@selector(foo)' vs '@selector( foo )' 658 | # Also applies to @protocol() constructs 659 | sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force 660 | 661 | # Add or remove space before a block pointer caret 662 | # '^int (int arg){...}' vs. ' ^int (int arg){...}' 663 | sp_before_oc_block_caret = ignore # ignore/add/remove/force 664 | 665 | # Add or remove space after a block pointer caret 666 | # '^int (int arg){...}' vs. '^ int (int arg){...}' 667 | sp_after_oc_block_caret = ignore # ignore/add/remove/force 668 | 669 | # Add or remove space between the receiver and selector in a message. 670 | # '[receiver selector ...]' 671 | sp_after_oc_msg_receiver = ignore # ignore/add/remove/force 672 | 673 | # Add or remove space after @property. 674 | sp_after_oc_property = ignore # ignore/add/remove/force 675 | 676 | # Add or remove space around the ':' in 'b ? t : f' 677 | sp_cond_colon = force # ignore/add/remove/force 678 | 679 | # Add or remove space around the '?' in 'b ? t : f' 680 | sp_cond_question = force # ignore/add/remove/force 681 | 682 | # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. 683 | sp_case_label = ignore # ignore/add/remove/force 684 | 685 | # Control the space around the D '..' operator. 686 | sp_range = ignore # ignore/add/remove/force 687 | 688 | # Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java) 689 | sp_after_for_colon = ignore # ignore/add/remove/force 690 | 691 | # Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java) 692 | sp_before_for_colon = ignore # ignore/add/remove/force 693 | 694 | # Control the spacing in 'extern (C)' (D) 695 | sp_extern_paren = ignore # ignore/add/remove/force 696 | 697 | # Control the space after the opening of a C++ comment '// A' vs '//A' 698 | sp_cmt_cpp_start = ignore # ignore/add/remove/force 699 | 700 | # Controls the spaces between #else or #endif and a trailing comment 701 | sp_endif_cmt = ignore # ignore/add/remove/force 702 | 703 | # Controls the spaces after 'new', 'delete', and 'delete[]' 704 | sp_after_new = ignore # ignore/add/remove/force 705 | 706 | # Controls the spaces before a trailing or embedded comment 707 | sp_before_tr_emb_cmt = ignore # ignore/add/remove/force 708 | 709 | # Number of spaces before a trailing or embedded comment 710 | sp_num_before_tr_emb_cmt = 0 # number 711 | 712 | # Control space between a Java annotation and the open paren. 713 | sp_annotation_paren = ignore # ignore/add/remove/force 714 | 715 | # 716 | # Code alignment (not left column spaces/tabs) 717 | # 718 | 719 | # Whether to keep non-indenting tabs 720 | align_keep_tabs = false # false/true 721 | 722 | # Whether to use tabs for aligning 723 | align_with_tabs = false # false/true 724 | 725 | # Whether to bump out to the next tab when aligning 726 | align_on_tabstop = false # false/true 727 | 728 | # Whether to left-align numbers 729 | align_number_left = false # false/true 730 | 731 | # Whether to keep whitespace not required for alignment. 732 | align_keep_extra_space = false # false/true 733 | 734 | # Align variable definitions in prototypes and functions 735 | align_func_params = false # false/true 736 | 737 | # Align parameters in single-line functions that have the same name. 738 | # The function names must already be aligned with each other. 739 | align_same_func_call_params = false # false/true 740 | 741 | # The span for aligning variable definitions (0=don't align) 742 | align_var_def_span = 0 # number 743 | 744 | # How to align the star in variable definitions. 745 | # 0=Part of the type 'void * foo;' 746 | # 1=Part of the variable 'void *foo;' 747 | # 2=Dangling 'void *foo;' 748 | align_var_def_star_style = 1 # number 749 | 750 | # How to align the '&' in variable definitions. 751 | # 0=Part of the type 752 | # 1=Part of the variable 753 | # 2=Dangling 754 | align_var_def_amp_style = 0 # number 755 | 756 | # The threshold for aligning variable definitions (0=no limit) 757 | align_var_def_thresh = 0 # number 758 | 759 | # The gap for aligning variable definitions 760 | align_var_def_gap = 0 # number 761 | 762 | # Whether to align the colon in struct bit fields 763 | align_var_def_colon = false # false/true 764 | 765 | # Whether to align any attribute after the variable name 766 | align_var_def_attribute = false # false/true 767 | 768 | # Whether to align inline struct/enum/union variable definitions 769 | align_var_def_inline = false # false/true 770 | 771 | # The span for aligning on '=' in assignments (0=don't align) 772 | align_assign_span = 0 # number 773 | 774 | # The threshold for aligning on '=' in assignments (0=no limit) 775 | align_assign_thresh = 0 # number 776 | 777 | # The span for aligning on '=' in enums (0=don't align) 778 | align_enum_equ_span = 0 # number 779 | 780 | # The threshold for aligning on '=' in enums (0=no limit) 781 | align_enum_equ_thresh = 0 # number 782 | 783 | # The span for aligning struct/union (0=don't align) 784 | align_var_struct_span = 0 # number 785 | 786 | # The threshold for aligning struct/union member definitions (0=no limit) 787 | align_var_struct_thresh = 0 # number 788 | 789 | # The gap for aligning struct/union member definitions 790 | align_var_struct_gap = 0 # number 791 | 792 | # The span for aligning struct initializer values (0=don't align) 793 | align_struct_init_span = 0 # number 794 | 795 | # The minimum space between the type and the synonym of a typedef 796 | align_typedef_gap = 0 # number 797 | 798 | # The span for aligning single-line typedefs (0=don't align) 799 | align_typedef_span = 0 # number 800 | 801 | # How to align typedef'd functions with other typedefs 802 | # 0: Don't mix them at all 803 | # 1: align the open paren with the types 804 | # 2: align the function type name with the other type names 805 | align_typedef_func = 0 # number 806 | 807 | # Controls the positioning of the '*' in typedefs. Just try it. 808 | # 0: Align on typedef type, ignore '*' 809 | # 1: The '*' is part of type name: typedef int *pint; 810 | # 2: The '*' is part of the type, but dangling: typedef int *pint; 811 | align_typedef_star_style = 0 # number 812 | 813 | # Controls the positioning of the '&' in typedefs. Just try it. 814 | # 0: Align on typedef type, ignore '&' 815 | # 1: The '&' is part of type name: typedef int &pint; 816 | # 2: The '&' is part of the type, but dangling: typedef int &pint; 817 | align_typedef_amp_style = 0 # number 818 | 819 | # The span for aligning comments that end lines (0=don't align) 820 | align_right_cmt_span = 0 # number 821 | 822 | # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment 823 | align_right_cmt_mix = false # false/true 824 | 825 | # If a trailing comment is more than this number of columns away from the text it follows, 826 | # it will qualify for being aligned. This has to be > 0 to do anything. 827 | align_right_cmt_gap = 0 # number 828 | 829 | # Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) 830 | align_right_cmt_at_col = 0 # number 831 | 832 | # The span for aligning function prototypes (0=don't align) 833 | align_func_proto_span = 0 # number 834 | 835 | # Minimum gap between the return type and the function name. 836 | align_func_proto_gap = 0 # number 837 | 838 | # Align function protos on the 'operator' keyword instead of what follows 839 | align_on_operator = false # false/true 840 | 841 | # Whether to mix aligning prototype and variable declarations. 842 | # If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. 843 | align_mix_var_proto = false # false/true 844 | 845 | # Align single-line functions with function prototypes, uses align_func_proto_span 846 | align_single_line_func = false # false/true 847 | 848 | # Aligning the open brace of single-line functions. 849 | # Requires align_single_line_func=true, uses align_func_proto_span 850 | align_single_line_brace = false # false/true 851 | 852 | # Gap for align_single_line_brace. 853 | align_single_line_brace_gap = 0 # number 854 | 855 | # The span for aligning ObjC msg spec (0=don't align) 856 | align_oc_msg_spec_span = 0 # number 857 | 858 | # Whether to align macros wrapped with a backslash and a newline. 859 | # This will not work right if the macro contains a multi-line comment. 860 | align_nl_cont = false # false/true 861 | 862 | # # Align macro functions and variables together 863 | align_pp_define_together = false # false/true 864 | 865 | # The minimum space between label and value of a preprocessor define 866 | align_pp_define_gap = 0 # number 867 | 868 | # The span for aligning on '#define' bodies (0=don't align) 869 | align_pp_define_span = 0 # number 870 | 871 | # Align lines that start with '<<' with previous '<<'. Default=true 872 | align_left_shift = true # false/true 873 | 874 | # Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) 875 | align_oc_msg_colon_span = 0 # number 876 | 877 | # If true, always align with the first parameter, even if it is too short. 878 | align_oc_msg_colon_first = false # false/true 879 | 880 | # Aligning parameters in an Obj-C '+' or '-' declaration on the ':' 881 | align_oc_decl_colon = false # false/true 882 | 883 | # 884 | # Newline adding and removing options 885 | # 886 | 887 | # Whether to collapse empty blocks between '{' and '}' 888 | nl_collapse_empty_body = true # false/true 889 | 890 | # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' 891 | nl_assign_leave_one_liners = true # false/true 892 | 893 | # Don't split one-line braced statements inside a class xx { } body 894 | nl_class_leave_one_liners = false # false/true 895 | 896 | # Don't split one-line enums: 'enum foo { BAR = 15 };' 897 | nl_enum_leave_one_liners = false # false/true 898 | 899 | # Don't split one-line get or set functions 900 | nl_getset_leave_one_liners = false # false/true 901 | 902 | # Don't split one-line function definitions - 'int foo() { return 0; }' 903 | nl_func_leave_one_liners = false # false/true 904 | 905 | # Don't split one-line C++11 lambdas - '[]() { return 0; }' 906 | nl_cpp_lambda_leave_one_liners = false # false/true 907 | 908 | # Don't split one-line if/else statements - 'if(a) b++;' 909 | nl_if_leave_one_liners = false # false/true 910 | 911 | # Don't split one-line OC messages 912 | nl_oc_msg_leave_one_liner = false # false/true 913 | 914 | # Add or remove newlines at the start of the file 915 | nl_start_of_file = remove # ignore/add/remove/force 916 | 917 | # The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' 918 | nl_start_of_file_min = 0 # number 919 | 920 | # Add or remove newline at the end of the file 921 | nl_end_of_file = add # ignore/add/remove/force 922 | 923 | # The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') 924 | nl_end_of_file_min = 0 # number 925 | 926 | # Add or remove newline between '=' and '{' 927 | nl_assign_brace = remove # ignore/add/remove/force 928 | 929 | # Add or remove newline between '=' and '[' (D only) 930 | nl_assign_square = ignore # ignore/add/remove/force 931 | 932 | # Add or remove newline after '= [' (D only). Will also affect the newline before the ']' 933 | nl_after_square_assign = ignore # ignore/add/remove/force 934 | 935 | # The number of blank lines after a block of variable definitions at the top of a function body 936 | # 0 = No change (default) 937 | nl_func_var_def_blk = 0 # number 938 | 939 | # The number of newlines before a block of typedefs 940 | # 0 = No change (default) 941 | nl_typedef_blk_start = 0 # number 942 | 943 | # The number of newlines after a block of typedefs 944 | # 0 = No change (default) 945 | nl_typedef_blk_end = 0 # number 946 | 947 | # The maximum consecutive newlines within a block of typedefs 948 | # 0 = No change (default) 949 | nl_typedef_blk_in = 0 # number 950 | 951 | # The number of newlines before a block of variable definitions not at the top of a function body 952 | # 0 = No change (default) 953 | nl_var_def_blk_start = 0 # number 954 | 955 | # The number of newlines after a block of variable definitions not at the top of a function body 956 | # 0 = No change (default) 957 | nl_var_def_blk_end = 0 # number 958 | 959 | # The maximum consecutive newlines within a block of variable definitions 960 | # 0 = No change (default) 961 | nl_var_def_blk_in = 0 # number 962 | 963 | # Add or remove newline between a function call's ')' and '{', as in: 964 | # list_for_each(item, &list) { } 965 | nl_fcall_brace = remove # ignore/add/remove/force 966 | 967 | # Add or remove newline between 'enum' and '{' 968 | nl_enum_brace = remove # ignore/add/remove/force 969 | 970 | # Add or remove newline between 'struct and '{' 971 | nl_struct_brace = remove # ignore/add/remove/force 972 | 973 | # Add or remove newline between 'union' and '{' 974 | nl_union_brace = remove # ignore/add/remove/force 975 | 976 | # Add or remove newline between 'if' and '{' 977 | nl_if_brace = remove # ignore/add/remove/force 978 | 979 | # Add or remove newline between '}' and 'else' 980 | nl_brace_else = force # ignore/add/remove/force 981 | 982 | # Add or remove newline between 'else if' and '{' 983 | # If set to ignore, nl_if_brace is used instead 984 | nl_elseif_brace = ignore # ignore/add/remove/force 985 | 986 | # Add or remove newline between 'else' and '{' 987 | nl_else_brace = remove # ignore/add/remove/force 988 | 989 | # Add or remove newline between 'else' and 'if' 990 | nl_else_if = remove # ignore/add/remove/force 991 | 992 | # Add or remove newline between '}' and 'finally' 993 | nl_brace_finally = ignore # ignore/add/remove/force 994 | 995 | # Add or remove newline between 'finally' and '{' 996 | nl_finally_brace = ignore # ignore/add/remove/force 997 | 998 | # Add or remove newline between 'try' and '{' 999 | nl_try_brace = ignore # ignore/add/remove/force 1000 | 1001 | # Add or remove newline between get/set and '{' 1002 | nl_getset_brace = ignore # ignore/add/remove/force 1003 | 1004 | # Add or remove newline between 'for' and '{' 1005 | nl_for_brace = remove # ignore/add/remove/force 1006 | 1007 | # Add or remove newline between 'catch' and '{' 1008 | nl_catch_brace = ignore # ignore/add/remove/force 1009 | 1010 | # Add or remove newline between '}' and 'catch' 1011 | nl_brace_catch = ignore # ignore/add/remove/force 1012 | 1013 | # Add or remove newline between 'while' and '{' 1014 | nl_while_brace = remove # ignore/add/remove/force 1015 | 1016 | # Add or remove newline between 'scope (x)' and '{' (D) 1017 | nl_scope_brace = ignore # ignore/add/remove/force 1018 | 1019 | # Add or remove newline between 'unittest' and '{' (D) 1020 | nl_unittest_brace = ignore # ignore/add/remove/force 1021 | 1022 | # Add or remove newline between 'version (x)' and '{' (D) 1023 | nl_version_brace = ignore # ignore/add/remove/force 1024 | 1025 | # Add or remove newline between 'using' and '{' 1026 | nl_using_brace = ignore # ignore/add/remove/force 1027 | 1028 | # Add or remove newline between two open or close braces. 1029 | # Due to general newline/brace handling, REMOVE may not work. 1030 | nl_brace_brace = ignore # ignore/add/remove/force 1031 | 1032 | # Add or remove newline between 'do' and '{' 1033 | nl_do_brace = remove # ignore/add/remove/force 1034 | 1035 | # Add or remove newline between '}' and 'while' of 'do' statement 1036 | nl_brace_while = remove # ignore/add/remove/force 1037 | 1038 | # Add or remove newline between 'switch' and '{' 1039 | nl_switch_brace = remove # ignore/add/remove/force 1040 | 1041 | # Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. 1042 | # Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. 1043 | nl_multi_line_cond = false # false/true 1044 | 1045 | # Force a newline in a define after the macro name for multi-line defines. 1046 | nl_multi_line_define = false # false/true 1047 | 1048 | # Whether to put a newline before 'case' statement 1049 | nl_before_case = false # false/true 1050 | 1051 | # Add or remove newline between ')' and 'throw' 1052 | nl_before_throw = ignore # ignore/add/remove/force 1053 | 1054 | # Whether to put a newline after 'case' statement 1055 | nl_after_case = false # false/true 1056 | 1057 | # Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. 1058 | nl_case_colon_brace = ignore # ignore/add/remove/force 1059 | 1060 | # Newline between namespace and { 1061 | nl_namespace_brace = ignore # ignore/add/remove/force 1062 | 1063 | # Add or remove newline between 'template<>' and whatever follows. 1064 | nl_template_class = ignore # ignore/add/remove/force 1065 | 1066 | # Add or remove newline between 'class' and '{' 1067 | nl_class_brace = ignore # ignore/add/remove/force 1068 | 1069 | # Add or remove newline after each ',' in the constructor member initialization 1070 | nl_class_init_args = ignore # ignore/add/remove/force 1071 | 1072 | # Add or remove newline between return type and function name in a function definition 1073 | nl_func_type_name = force # ignore/add/remove/force 1074 | 1075 | # Add or remove newline between return type and function name inside a class {} 1076 | # Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. 1077 | nl_func_type_name_class = ignore # ignore/add/remove/force 1078 | 1079 | # Add or remove newline between function scope and name in a definition 1080 | # Controls the newline after '::' in 'void A::f() { }' 1081 | nl_func_scope_name = ignore # ignore/add/remove/force 1082 | 1083 | # Add or remove newline between return type and function name in a prototype 1084 | nl_func_proto_type_name = remove # ignore/add/remove/force 1085 | 1086 | # Add or remove newline between a function name and the opening '(' 1087 | nl_func_paren = remove # ignore/add/remove/force 1088 | 1089 | # Add or remove newline between a function name and the opening '(' in the definition 1090 | nl_func_def_paren = remove # ignore/add/remove/force 1091 | 1092 | # Add or remove newline after '(' in a function declaration 1093 | nl_func_decl_start = remove # ignore/add/remove/force 1094 | 1095 | # Add or remove newline after '(' in a function definition 1096 | nl_func_def_start = remove # ignore/add/remove/force 1097 | 1098 | # Overrides nl_func_decl_start when there is only one parameter. 1099 | nl_func_decl_start_single = ignore # ignore/add/remove/force 1100 | 1101 | # Overrides nl_func_def_start when there is only one parameter. 1102 | nl_func_def_start_single = ignore # ignore/add/remove/force 1103 | 1104 | # Add or remove newline after each ',' in a function declaration 1105 | nl_func_decl_args = ignore # ignore/add/remove/force 1106 | 1107 | # Add or remove newline after each ',' in a function definition 1108 | nl_func_def_args = ignore # ignore/add/remove/force 1109 | 1110 | # Add or remove newline before the ')' in a function declaration 1111 | nl_func_decl_end = remove # ignore/add/remove/force 1112 | 1113 | # Add or remove newline before the ')' in a function definition 1114 | nl_func_def_end = remove # ignore/add/remove/force 1115 | 1116 | # Overrides nl_func_decl_end when there is only one parameter. 1117 | nl_func_decl_end_single = ignore # ignore/add/remove/force 1118 | 1119 | # Overrides nl_func_def_end when there is only one parameter. 1120 | nl_func_def_end_single = ignore # ignore/add/remove/force 1121 | 1122 | # Add or remove newline between '()' in a function declaration. 1123 | nl_func_decl_empty = remove # ignore/add/remove/force 1124 | 1125 | # Add or remove newline between '()' in a function definition. 1126 | nl_func_def_empty = remove # ignore/add/remove/force 1127 | 1128 | # Whether to put each OC message parameter on a separate line 1129 | # See nl_oc_msg_leave_one_liner 1130 | nl_oc_msg_args = false # false/true 1131 | 1132 | # Add or remove newline between function signature and '{' 1133 | nl_fdef_brace = remove # ignore/add/remove/force 1134 | 1135 | # Add or remove newline between C++11 lambda signature and '{' 1136 | nl_cpp_ldef_brace = ignore # ignore/add/remove/force 1137 | 1138 | # Add or remove a newline between the return keyword and return expression. 1139 | nl_return_expr = ignore # ignore/add/remove/force 1140 | 1141 | # Whether to put a newline after semicolons, except in 'for' statements 1142 | nl_after_semicolon = true # false/true 1143 | 1144 | # Whether to put a newline after brace open. 1145 | # This also adds a newline before the matching brace close. 1146 | nl_after_brace_open = false # false/true 1147 | 1148 | # If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is 1149 | # placed between the open brace and a trailing single-line comment. 1150 | nl_after_brace_open_cmt = false # false/true 1151 | 1152 | # Whether to put a newline after a virtual brace open with a non-empty body. 1153 | # These occur in un-braced if/while/do/for statement bodies. 1154 | nl_after_vbrace_open = false # false/true 1155 | 1156 | # Whether to put a newline after a virtual brace open with an empty body. 1157 | # These occur in un-braced if/while/do/for statement bodies. 1158 | nl_after_vbrace_open_empty = false # false/true 1159 | 1160 | # Whether to put a newline after a brace close. 1161 | # Does not apply if followed by a necessary ';'. 1162 | nl_after_brace_close = false # false/true 1163 | 1164 | # Whether to put a newline after a virtual brace close. 1165 | # Would add a newline before return in: 'if (foo) a++; return;' 1166 | nl_after_vbrace_close = false # false/true 1167 | 1168 | # Control the newline between the close brace and 'b' in: 'struct { int a; } b;' 1169 | # Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close 1170 | nl_brace_struct_var = ignore # ignore/add/remove/force 1171 | 1172 | # Whether to alter newlines in '#define' macros 1173 | nl_define_macro = false # false/true 1174 | 1175 | # Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' 1176 | nl_squeeze_ifdef = false # false/true 1177 | 1178 | # Add or remove blank line before 'if' 1179 | nl_before_if = ignore # ignore/add/remove/force 1180 | 1181 | # Add or remove blank line after 'if' statement 1182 | nl_after_if = ignore # ignore/add/remove/force 1183 | 1184 | # Add or remove blank line before 'for' 1185 | nl_before_for = ignore # ignore/add/remove/force 1186 | 1187 | # Add or remove blank line after 'for' statement 1188 | nl_after_for = ignore # ignore/add/remove/force 1189 | 1190 | # Add or remove blank line before 'while' 1191 | nl_before_while = ignore # ignore/add/remove/force 1192 | 1193 | # Add or remove blank line after 'while' statement 1194 | nl_after_while = ignore # ignore/add/remove/force 1195 | 1196 | # Add or remove blank line before 'switch' 1197 | nl_before_switch = ignore # ignore/add/remove/force 1198 | 1199 | # Add or remove blank line after 'switch' statement 1200 | nl_after_switch = ignore # ignore/add/remove/force 1201 | 1202 | # Add or remove blank line before 'do' 1203 | nl_before_do = ignore # ignore/add/remove/force 1204 | 1205 | # Add or remove blank line after 'do/while' statement 1206 | nl_after_do = ignore # ignore/add/remove/force 1207 | 1208 | # Whether to double-space commented-entries in struct/enum 1209 | nl_ds_struct_enum_cmt = false # false/true 1210 | 1211 | # Whether to double-space before the close brace of a struct/union/enum 1212 | # (lower priority than 'eat_blanks_before_close_brace') 1213 | nl_ds_struct_enum_close_brace = false # false/true 1214 | 1215 | # Add or remove a newline around a class colon. 1216 | # Related to pos_class_colon, nl_class_init_args, and pos_comma. 1217 | nl_class_colon = ignore # ignore/add/remove/force 1218 | 1219 | # Change simple unbraced if statements into a one-liner 1220 | # 'if(b)\n i++;' => 'if(b) i++;' 1221 | nl_create_if_one_liner = false # false/true 1222 | 1223 | # Change simple unbraced for statements into a one-liner 1224 | # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' 1225 | nl_create_for_one_liner = false # false/true 1226 | 1227 | # Change simple unbraced while statements into a one-liner 1228 | # 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' 1229 | nl_create_while_one_liner = false # false/true 1230 | 1231 | # 1232 | # Positioning options 1233 | # 1234 | 1235 | # The position of arithmetic operators in wrapped expressions 1236 | pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1237 | 1238 | # The position of assignment in wrapped expressions. 1239 | # Do not affect '=' followed by '{' 1240 | pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1241 | 1242 | # The position of boolean operators in wrapped expressions 1243 | pos_bool = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1244 | 1245 | # The position of comparison operators in wrapped expressions 1246 | pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1247 | 1248 | # The position of conditional (b ? t : f) operators in wrapped expressions 1249 | pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1250 | 1251 | # The position of the comma in wrapped expressions 1252 | pos_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1253 | 1254 | # The position of the comma in the constructor initialization list 1255 | pos_class_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1256 | 1257 | # The position of colons between constructor and member initialization 1258 | pos_class_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force 1259 | 1260 | # 1261 | # Line Splitting options 1262 | # 1263 | 1264 | # Try to limit code width to N number of columns 1265 | code_width = 0 # number 1266 | 1267 | # Whether to fully split long 'for' statements at semi-colons 1268 | ls_for_split_full = false # false/true 1269 | 1270 | # Whether to fully split long function protos/calls at commas 1271 | ls_func_split_full = false # false/true 1272 | 1273 | # Whether to split lines as close to code_width as possible and ignore some groupings 1274 | ls_code_width = false # false/true 1275 | 1276 | # 1277 | # Blank line options 1278 | # 1279 | 1280 | # The maximum consecutive newlines 1281 | nl_max = 2 # number 1282 | 1283 | # The number of newlines after a function prototype, if followed by another function prototype 1284 | nl_after_func_proto = 1 # number 1285 | 1286 | # The number of newlines after a function prototype, if not followed by another function prototype 1287 | nl_after_func_proto_group = 2 # number 1288 | 1289 | # The number of newlines after '}' of a multi-line function body 1290 | nl_after_func_body = 2 # number 1291 | 1292 | # The number of newlines after '}' of a multi-line function body in a class declaration 1293 | nl_after_func_body_class = 2 # number 1294 | 1295 | # The number of newlines after '}' of a single line function body 1296 | nl_after_func_body_one_liner = 0 # number 1297 | 1298 | # The minimum number of newlines before a multi-line comment. 1299 | # Doesn't apply if after a brace open or another multi-line comment. 1300 | nl_before_block_comment = 2 # number 1301 | 1302 | # The minimum number of newlines before a single-line C comment. 1303 | # Doesn't apply if after a brace open or other single-line C comments. 1304 | nl_before_c_comment = 2 # number 1305 | 1306 | # The minimum number of newlines before a CPP comment. 1307 | # Doesn't apply if after a brace open or other CPP comments. 1308 | nl_before_cpp_comment = 2 # number 1309 | 1310 | # Whether to force a newline after a multi-line comment. 1311 | nl_after_multiline_comment = true # false/true 1312 | 1313 | # The number of newlines after '}' or ';' of a struct/enum/union definition 1314 | nl_after_struct = 0 # number 1315 | 1316 | # The number of newlines after '}' or ';' of a class definition 1317 | nl_after_class = 0 # number 1318 | 1319 | # The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. 1320 | # Will not change the newline count if after a brace open. 1321 | # 0 = No change. 1322 | nl_before_access_spec = 0 # number 1323 | 1324 | # The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. 1325 | # 0 = No change. 1326 | nl_after_access_spec = 0 # number 1327 | 1328 | # The number of newlines between a function def and the function comment. 1329 | # 0 = No change. 1330 | nl_comment_func_def = 0 # number 1331 | 1332 | # The number of newlines after a try-catch-finally block that isn't followed by a brace close. 1333 | # 0 = No change. 1334 | nl_after_try_catch_finally = 0 # number 1335 | 1336 | # The number of newlines before and after a property, indexer or event decl. 1337 | # 0 = No change. 1338 | nl_around_cs_property = 0 # number 1339 | 1340 | # The number of newlines between the get/set/add/remove handlers in C#. 1341 | # 0 = No change. 1342 | nl_between_get_set = 0 # number 1343 | 1344 | # Add or remove newline between C# property and the '{' 1345 | nl_property_brace = ignore # ignore/add/remove/force 1346 | 1347 | # Whether to remove blank lines after '{' 1348 | eat_blanks_after_open_brace = true # false/true 1349 | 1350 | # Whether to remove blank lines before '}' 1351 | eat_blanks_before_close_brace = true # false/true 1352 | 1353 | # How aggressively to remove extra newlines not in preproc. 1354 | # 0: No change 1355 | # 1: Remove most newlines not handled by other config 1356 | # 2: Remove all newlines and reformat completely by config 1357 | nl_remove_extra_newlines = 0 # number 1358 | 1359 | # Whether to put a blank line before 'return' statements, unless after an open brace. 1360 | nl_before_return = true # false/true 1361 | 1362 | # Whether to put a blank line after 'return' statements, unless followed by a close brace. 1363 | nl_after_return = false # false/true 1364 | 1365 | # Whether to put a newline after a Java annotation statement. 1366 | # Only affects annotations that are after a newline. 1367 | nl_after_annotation = ignore # ignore/add/remove/force 1368 | 1369 | # Controls the newline between two annotations. 1370 | nl_between_annotation = ignore # ignore/add/remove/force 1371 | 1372 | # 1373 | # Code modifying options (non-whitespace) 1374 | # 1375 | 1376 | # Add or remove braces on single-line 'do' statement 1377 | mod_full_brace_do = add # ignore/add/remove/force 1378 | 1379 | # Add or remove braces on single-line 'for' statement 1380 | mod_full_brace_for = add # ignore/add/remove/force 1381 | 1382 | # Add or remove braces on single-line function definitions. (Pawn) 1383 | mod_full_brace_function = add # ignore/add/remove/force 1384 | 1385 | # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. 1386 | mod_full_brace_if = add # ignore/add/remove/force 1387 | 1388 | # Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. 1389 | # If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. 1390 | mod_full_brace_if_chain = false # false/true 1391 | 1392 | # Don't remove braces around statements that span N newlines 1393 | mod_full_brace_nl = 0 # number 1394 | 1395 | # Add or remove braces on single-line 'while' statement 1396 | mod_full_brace_while = add # ignore/add/remove/force 1397 | 1398 | # Add or remove braces on single-line 'using ()' statement 1399 | mod_full_brace_using = ignore # ignore/add/remove/force 1400 | 1401 | # Add or remove unnecessary paren on 'return' statement 1402 | mod_paren_on_return = remove # ignore/add/remove/force 1403 | 1404 | # Whether to change optional semicolons to real semicolons 1405 | mod_pawn_semicolon = true # false/true 1406 | 1407 | # Add parens on 'while' and 'if' statement around bools 1408 | mod_full_paren_if_bool = true # false/true 1409 | 1410 | # Whether to remove superfluous semicolons 1411 | mod_remove_extra_semicolon = false # false/true 1412 | 1413 | # If a function body exceeds the specified number of newlines and doesn't have a comment after 1414 | # the close brace, a comment will be added. 1415 | mod_add_long_function_closebrace_comment = 0 # number 1416 | 1417 | # If a switch body exceeds the specified number of newlines and doesn't have a comment after 1418 | # the close brace, a comment will be added. 1419 | mod_add_long_switch_closebrace_comment = 0 # number 1420 | 1421 | # If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after 1422 | # the #endif, a comment will be added. 1423 | mod_add_long_ifdef_endif_comment = 0 # number 1424 | 1425 | # If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after 1426 | # the #else, a comment will be added. 1427 | mod_add_long_ifdef_else_comment = 0 # number 1428 | 1429 | # If TRUE, will sort consecutive single-line 'import' statements [Java, D] 1430 | mod_sort_import = false # false/true 1431 | 1432 | # If TRUE, will sort consecutive single-line 'using' statements [C#] 1433 | mod_sort_using = false # false/true 1434 | 1435 | # If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] 1436 | # This is generally a bad idea, as it may break your code. 1437 | mod_sort_include = false # false/true 1438 | 1439 | # If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. 1440 | mod_move_case_break = false # false/true 1441 | 1442 | # Will add or remove the braces around a fully braced case statement. 1443 | # Will only remove the braces if there are no variable declarations in the block. 1444 | mod_case_brace = ignore # ignore/add/remove/force 1445 | 1446 | # If TRUE, it will remove a void 'return;' that appears as the last statement in a function. 1447 | mod_remove_empty_return = true # false/true 1448 | 1449 | # 1450 | # Comment modifications 1451 | # 1452 | 1453 | # Try to wrap comments at cmt_width columns 1454 | cmt_width = 80 # number 1455 | 1456 | # Set the comment reflow mode (default: 0) 1457 | # 0: no reflowing (apart from the line wrapping due to cmt_width) 1458 | # 1: no touching at all 1459 | # 2: full reflow 1460 | cmt_reflow_mode = 2 # number 1461 | 1462 | # If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars. 1463 | # Default is true. 1464 | cmt_indent_multi = true # false/true 1465 | 1466 | # Whether to group c-comments that look like they are in a block 1467 | cmt_c_group = true # false/true 1468 | 1469 | # Whether to put an empty '/*' on the first line of the combined c-comment 1470 | cmt_c_nl_start = false # false/true 1471 | 1472 | # Whether to put a newline before the closing '*/' of the combined c-comment 1473 | cmt_c_nl_end = false # false/true 1474 | 1475 | # Whether to group cpp-comments that look like they are in a block 1476 | cmt_cpp_group = false # false/true 1477 | 1478 | # Whether to put an empty '/*' on the first line of the combined cpp-comment 1479 | cmt_cpp_nl_start = false # false/true 1480 | 1481 | # Whether to put a newline before the closing '*/' of the combined cpp-comment 1482 | cmt_cpp_nl_end = false # false/true 1483 | 1484 | # Whether to change cpp-comments into c-comments 1485 | cmt_cpp_to_c = true # false/true 1486 | 1487 | # Whether to put a star on subsequent comment lines 1488 | cmt_star_cont = false # false/true 1489 | 1490 | # The number of spaces to insert at the start of subsequent comment lines 1491 | cmt_sp_before_star_cont = 0 # number 1492 | 1493 | # The number of spaces to insert after the star on subsequent comment lines 1494 | cmt_sp_after_star_cont = 0 # number 1495 | 1496 | # For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of 1497 | # the comment are the same length. Default=True 1498 | cmt_multi_check_last = true # false/true 1499 | 1500 | # The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. 1501 | # Will substitute $(filename) with the current file's name. 1502 | cmt_insert_file_header = "" # string 1503 | 1504 | # The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. 1505 | # Will substitute $(filename) with the current file's name. 1506 | cmt_insert_file_footer = "" # string 1507 | 1508 | # The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. 1509 | # Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. 1510 | # Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } 1511 | cmt_insert_func_header = "" # string 1512 | 1513 | # The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. 1514 | # Will substitute $(class) with the class name. 1515 | cmt_insert_class_header = "" # string 1516 | 1517 | # The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment. 1518 | # Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. 1519 | cmt_insert_oc_msg_header = "" # string 1520 | 1521 | # If a preprocessor is encountered when stepping backwards from a function name, then 1522 | # this option decides whether the comment should be inserted. 1523 | # Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. 1524 | cmt_insert_before_preproc = false # false/true 1525 | 1526 | # 1527 | # Preprocessor options 1528 | # 1529 | 1530 | # Control indent of preprocessors inside #if blocks at brace level 0 1531 | pp_indent = force # ignore/add/remove/force 1532 | 1533 | # Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) 1534 | pp_indent_at_level = false # false/true 1535 | 1536 | # If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. 1537 | pp_indent_count = 0 # number 1538 | 1539 | # Add or remove space after # based on pp_level of #if blocks 1540 | pp_space = remove # ignore/add/remove/force 1541 | 1542 | # Sets the number of spaces added with pp_space 1543 | pp_space_count = 0 # number 1544 | 1545 | # The indent for #region and #endregion in C# and '#pragma region' in C/C++ 1546 | pp_indent_region = 0 # number 1547 | 1548 | # Whether to indent the code between #region and #endregion 1549 | pp_region_indent_code = false # false/true 1550 | 1551 | # If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level 1552 | pp_indent_if = 0 # number 1553 | 1554 | # Control whether to indent the code between #if, #else and #endif when not at file-level 1555 | pp_if_indent_code = false # false/true 1556 | 1557 | # Whether to indent '#define' at the brace level (true) or from column 1 (false) 1558 | pp_define_at_level = false # false/true 1559 | 1560 | # You can force a token to be a type with the 'type' option. 1561 | # Example: 1562 | # type myfoo1 myfoo2 1563 | # 1564 | # You can create custom macro-based indentation using macro-open, 1565 | # macro-else and macro-close. 1566 | # Example: 1567 | # macro-open BEGIN_TEMPLATE_MESSAGE_MAP 1568 | # macro-open BEGIN_MESSAGE_MAP 1569 | # macro-close END_MESSAGE_MAP 1570 | # 1571 | # You can assign any keyword to any type with the set option. 1572 | # set func_call_user _ N_ 1573 | # 1574 | # The full syntax description of all custom definition config entries 1575 | # is shown below: 1576 | # 1577 | # define custom tokens as: 1578 | # - embed whitespace in token using '' escape character, or 1579 | # put token in quotes 1580 | # - these: ' " and ` are recognized as quote delimiters 1581 | # 1582 | # type token1 token2 token3 ... 1583 | # ^ optionally specify multiple tokens on a single line 1584 | # define def_token output_token 1585 | # ^ output_token is optional, then NULL is assumed 1586 | # macro-open token 1587 | # macro-close token 1588 | # macro-else token 1589 | # set id token1 token2 ... 1590 | # ^ optionally specify multiple tokens on a single line 1591 | # ^ id is one of the names in token_enum.h sans the CT_ prefix, 1592 | # e.g. PP_PRAGMA 1593 | # 1594 | # all tokens are separated by any mix of ',' commas, '=' equal signs 1595 | # and whitespace (space, tab) 1596 | # 1597 | -------------------------------------------------------------------------------- /CONTRIBUTING.asciidoc: -------------------------------------------------------------------------------- 1 | docs/CONTRIBUTING.asciidoc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Kim Silkebækken 2 | https://github.com/Lokaltog/candybar 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.asciidoc: -------------------------------------------------------------------------------- 1 | docs/candybar.1.asciidoc -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | candybar.1 2 | candybar.5 3 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.asciidoc: -------------------------------------------------------------------------------- 1 | = Contributing 2 | 3 | == Requirements 4 | 5 | A basic knowledge of C is required for contributing to the core or for creating 6 | widgets. 7 | 8 | Knowledge of HTML, CSS and JS is required for contributing to candybar themes - see 9 | the https://github.com/Lokaltog/candybar-theme-default[default candybar theme] for 10 | details. 11 | 12 | == Making changes 13 | 14 | You need to be comfortable with git. The project uses the 15 | http://nvie.com/posts/a-successful-git-branching-model/[git flow] branching model. 16 | 17 | Create a topic branch from where you want to base your work. Most contributions 18 | should be based off the +develop+ branch. Prefix your branch with +feature/+ if 19 | you're working on a new feature. 20 | 21 | Submit a pull request to 22 | https://github.com/Lokaltog/candybar/pulls[Lokaltog/candybar]. Please write good, 23 | descriptive commit messages, and prefix your changes with the part the commit applies 24 | to, e.g. +widgets/weather: Fix errors when compiling with optimizations+. See 25 | http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[Tim Pope's guide 26 | to commit messages]. 27 | 28 | === Coding style 29 | 30 | The project uses a slightly modified BSD KNF coding style. It's recommended that you 31 | run +uncrustify+ with the config file provided in the project root on any modified or 32 | added C files before submitting a pull request. 33 | 34 | == Project layout 35 | 36 | All C source is located in +src/+. Every widget is located in a separate C file in 37 | +src/widgets/+. Common utility functions for logging, config loading, etc. are 38 | located in +src/util/+. 39 | 40 | +src/candybar.c+:: Loads user config, initializes the web view, sets window 41 | properties, etc. 42 | 43 | +src/widgets.c+:: Handles widget threads and JSCore extensions and callbacks. 44 | 45 | == Debug/development build 46 | 47 | This is useful for development as it uses a relative library search path, so candybar 48 | doesn't have to be installed system-wide for widgets to work. 49 | 50 | [source,sh] 51 | ---- 52 | ./waf configure clean build install \ # <1> 53 | --debug \ # <2> 54 | --prefix=/ \ # <3> 55 | --rootdir=`pwd`/out \ # <4> 56 | --destdir=out/ # <5> 57 | 58 | out/bin/candybar 59 | 60 | # ... 61 | 62 | ./waf build install # <6> 63 | ---- 64 | <1> the +configure+ and +clean+ actions are optional after the initial build 65 | <2> enable debug build (disable optimizations, enable debug logging) 66 | <3> disable prefixing the destination directory with +/usr/+ 67 | <4> tell +candybar+ to search for the default theme and widget libraries in the +out/+ directory 68 | <5> installs into +out/+, remember to change +--libdir+ if you change this directory 69 | <5> rebuild project, this command only recompiles changed files and is very fast 70 | -------------------------------------------------------------------------------- /docs/INSTALL.asciidoc: -------------------------------------------------------------------------------- 1 | == Dependencies 2 | 3 | * +gtk3+ 4 | * +jansson+ 5 | * +webkitgtk+ 6 | 7 | === Optional widget dependencies 8 | 9 | [options='header'] 10 | |=== 11 | |Widget |Dependencies 12 | |+battery+ 13 | |+libdbus+ 14 | 15 | |+desktops+ 16 | |+xcb+, +xcb-util-wm+ 17 | 18 | |+email_imap+ 19 | |+libcurl+ 20 | 21 | |+external_ip+ 22 | |+libcurl+ 23 | 24 | |+magick_background+ 25 | |+graphicsmagick+ 26 | 27 | |+notifications+ 28 | |+libdbus+ 29 | 30 | |+now_playing_mpd+ 31 | |+libmpdclient+ 32 | 33 | |+now_playing_mpris+ 34 | |+playerctl+ 35 | 36 | |+volume+ 37 | |+alsa+ 38 | 39 | |+weather+ 40 | |+libcurl+ 41 | 42 | |+window_title+ 43 | |+xcb+, +xcb-util-wm+ 44 | |=== 45 | 46 | == Installation 47 | 48 | [source,sh] 49 | ---- 50 | git clone https://github.com/Lokaltog/candybar.git 51 | cd candybar 52 | 53 | ./waf configure build 54 | ./waf install 55 | 56 | candybar 57 | ---- 58 | 59 | == Packages 60 | 61 | * Arch Linux 62 | ** https://aur.archlinux.org/packages/candybar-git/[candybar-git] 63 | -------------------------------------------------------------------------------- /docs/candybar.1.asciidoc: -------------------------------------------------------------------------------- 1 | :man source: candybar 2 | :man version: {revnumber} 3 | :man manual: Candybar Manual 4 | 5 | candybar(1) 6 | =========== 7 | 8 | Name 9 | ---- 10 | 11 | candybar - WebKit-based status bar for tiling window managers 12 | 13 | Synopsis 14 | -------- 15 | 16 | *candybar* [*-d*] [*-c* _config-file_] [*-h* _height_] [*-m* _monitor_] [*-p* 17 | _position_] [*-t* _theme-uri_] 18 | 19 | Description 20 | ----------- 21 | 22 | *candybar* displays a HTML-based status bar in a WebKit web view. It provides a 23 | smooth looking status bar with CSS transitions and effects, and the possibility of 24 | having a transparent background with effects without a desktop compositor running. 25 | 26 | Screenshots are available on the 27 | https://github.com/Lokaltog/candybar/wiki/Screenshots[candybar wiki]. 28 | 29 | Options 30 | ------- 31 | 32 | *-d*:: Enable debugging mode and display the WebKit inspector window on launch. 33 | 34 | *-c* _config-file_:: Read configuration options from the specified 35 | _config-file_. config-file overrides _CANDYBAR_CONFIG_PATH_ and any system-wide and 36 | per-user configuration files. The file must exist and be valid JSON. 37 | 38 | *-h* _height_:: Set the height of the status bar in pixels. 39 | 40 | *-m* _monitor_:: Define which monitor the status bar appears on. 41 | 42 | *-p* _position_:: Set the status bar position. Valid options are _top_ and _bottom_. 43 | 44 | *-t* _theme-uri_:: Set the URI for a theme HTML file. Must be a valid URI, i.e. local 45 | files must be prefixed with _file://_. 46 | 47 | Window manager support 48 | ---------------------- 49 | 50 | *bspwm*:: supported; requires bspwm to be configured to reserve space with _bspc 51 | config top_padding 24_. 52 | 53 | *dwm*:: supported; requires the dwm built-in bar to be enabled and set to the same 54 | position and height as candybar, candybar will be placed on top of the dwm bar. 55 | 56 | *i3*:: supported; desktop support requires the _desktops_i3_ widget to be enabled 57 | instead of the default _desktops_ widget for desktop list support. 58 | 59 | *monsterwm*:: supported 60 | 61 | *qtile*:: supported; requires a recent version, support for desktop EWMH properties 62 | was implemented in qtile revision 02fd471. 63 | 64 | Files 65 | ----- 66 | 67 | _~/.config/candybar/config.json_:: Per-user configuration file. See *candybar*(5) for 68 | further details. 69 | 70 | _/etc/xdg/candybar/config.json_:: System-wide configuration file. See *candybar*(5) 71 | for further details. 72 | 73 | Environment 74 | ----------- 75 | 76 | _CANDYBAR_CONFIG_PATH_:: Full path of an alternate _config.json_ file. This variable 77 | overrides any system-wide and per-user configuration files, but does not override the 78 | *-c* command-line argument. The file must exist and be valid JSON. 79 | 80 | Authors 81 | ------- 82 | 83 | Kim Silkebækken 84 | 85 | See https://github.com/Lokaltog/candybar/graphs/contributors for a list of all 86 | contributors. 87 | 88 | See also 89 | -------- 90 | 91 | *candybar*(5) 92 | -------------------------------------------------------------------------------- /docs/candybar.5.asciidoc: -------------------------------------------------------------------------------- 1 | :man source: candybar 2 | :man version: {revnumber} 3 | :man manual: Candybar Manual 4 | 5 | candybar(5) 6 | =========== 7 | 8 | Name 9 | ---- 10 | 11 | candybar - configuration file for customizing candybar 12 | 13 | Synopsis 14 | -------- 15 | 16 | _~/.config/candybar/config.json_ 17 | 18 | _/etc/xdg/candybar/config.json_ 19 | 20 | Description 21 | ----------- 22 | 23 | *config.json* is a JSON file that defines the appearance, enabled widgets and widget 24 | options for *candybar*(1). 25 | 26 | Configurable Widget Options 27 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28 | 29 | battery 30 | ^^^^^^^ 31 | 32 | Display battery and charging status. 33 | 34 | 'dbus_path':: 35 | Full dbus path to the battery or power device. 36 | 37 | default: "/org/freedesktop/UPower/devices/battery_BAT0" 38 | 39 | 'refresh_interval':: 40 | How often to check level (in seconds). 41 | 42 | default: 20 43 | 44 | datetime 45 | ^^^^^^^^ 46 | 47 | Display date and time. 48 | 49 | 'date_format':: 50 | Date format, see *strftime*(3) for syntax. 51 | 52 | default: "%Y-%m-%d" 53 | 54 | 'time_format':: 55 | Time format, see *strftime*(3) for syntax. 56 | 57 | default: "%H:%M" 58 | 59 | 'refresh_interval':: 60 | How often to refresh the status bar (in seconds). 61 | 62 | default: 10 63 | 64 | desktops 65 | ^^^^^^^^^^ 66 | 67 | Display desktops. 68 | 69 | 'show_empty':: 70 | Display empty desktops. Set to false to hide empty/unselected desktops. 71 | 72 | default: true 73 | 74 | email_imap 75 | ^^^^^^^^^^ 76 | 77 | Display unread e-mail count on an IMAP e-mail server. 78 | 79 | 'address':: 80 | URI of IMAP server. 81 | 82 | default: "imaps://imap.gmail.com:993/INBOX" 83 | 84 | 'username':: 85 | IMAP server user name. For GMail, an application-specific password is 86 | recommended. 87 | 88 | default: none 89 | 90 | 'password':: 91 | IMAP server password. 92 | 93 | default: none 94 | 95 | 'password_command':: 96 | Command to execute to retrieve the IMAP server password. Useful for GPG-encrypted 97 | password files or third-party password stores like *pass*(1). 98 | 99 | Example: "/usr/bin/pass path/to/my-imap-account" 100 | 101 | default: none 102 | 103 | 'ssl_verify':: 104 | Verify SSL certificate chain. 105 | 106 | default: true 107 | 108 | 'refresh_interval':: 109 | How often to check for new mail (in seconds). 110 | 111 | default: 60 112 | 113 | external_ip 114 | ^^^^^^^^^^^ 115 | 116 | Display your external IP address. 117 | 118 | 'address':: 119 | URI to query for IP address. The URI should return only the IP address as plain 120 | text. Recommended servers are "http://icanhazip.com", "http://ipv4.icanhazip.com" 121 | and "http://ipv6.icanhazip.com". 122 | 123 | default: "http://ipv4.icanhazip.com" 124 | 125 | 'refresh_interval':: 126 | How often to refresh IP address (in seconds). 127 | 128 | default: 3600 129 | 130 | magick_background 131 | ^^^^^^^^^^^^^^^^^ 132 | 133 | Make the status bar background transparent, and optionally applies effects like 134 | blurring, brightness adjustment and desaturation. 135 | 136 | 'blur_radius':: 137 | Gaussian blur radius. Recommended values are in the 0-10 range. 138 | 139 | default: 0 140 | 141 | 'brightness':: 142 | Brightness level. 100 is the default brightness level, <100 darkens the image, 143 | >100 brightens the image. 144 | 145 | default: 100 146 | 147 | 'saturation':: 148 | Saturation level. 100 is the default saturation level, <100 decreases the 149 | saturation level, >100 increases the saturation level. 150 | 151 | default: 100 152 | 153 | now_playing_mpd 154 | ^^^^^^^^^^^^^^^ 155 | 156 | Display the currently playing song on an MPD server. 157 | 158 | 'host':: 159 | MPD server host. 160 | 161 | default: localhost 162 | 163 | 'port':: 164 | MPD server port. 165 | 166 | default: 6600 167 | 168 | 'timeout':: 169 | MPD server connection timeout (in milliseconds). 170 | 171 | default: 5000 172 | 173 | now_playing_mpris 174 | ^^^^^^^^^^^^^^^^^ 175 | 176 | Display the currently playing song on an MPRIS-compatible media player. Some 177 | compatible players include clementine-player, XMMS2, Spotify. Plugins are also 178 | available for mopidy and VLC. 179 | 180 | 'player_name':: 181 | MPRIS player name (e.g. "Spotify"). 182 | 183 | volume 184 | ^^^^^^ 185 | 186 | Display the current ALSA volume level and mute status. 187 | 188 | 'card':: 189 | Sound card name. Run 'aplay -L' to receive a list of valid sound cards. 190 | 191 | default: "default" 192 | 193 | 'selem':: 194 | Which simple mixer control to read and adjust. Run 'amixer scontrols' to receive 195 | a list of valid simple mixer controls. 196 | 197 | default: "Master" 198 | 199 | weather 200 | ^^^^^^^ 201 | 202 | Display the current weather and temperature. Your location is detected automatically 203 | using a GeoIP lookup, but setting it manually is recommended if you're behind a 204 | proxy, and to avoid an extra request when updating the weather. The widget uses 205 | Yahoo! Weather for weather lookups. 206 | 207 | 'location':: 208 | ZIP code or location query (e.g. "Oslo, Norway"). 209 | 210 | default: none 211 | 212 | 'unit':: 213 | Farenheit ("f") or Celsius ("c"). 214 | 215 | default: "c" 216 | 217 | 'refresh_interval':: 218 | How often to check for updates (in seconds). 219 | 220 | default: 1800 221 | 222 | Files 223 | ----- 224 | 225 | _~/.config/candybar/config.json_:: Per-user configuration file. See *candybar*(5) for 226 | further details. 227 | 228 | _/etc/xdg/candybar/config.json_:: System-wide configuration file. See *candybar*(5) for 229 | further details. 230 | 231 | Authors 232 | ------- 233 | 234 | Kim Silkebækken 235 | 236 | See https://github.com/Lokaltog/candybar/graphs/contributors for a list of all 237 | contributors. 238 | 239 | See also 240 | -------- 241 | 242 | *candybar*(1) 243 | -------------------------------------------------------------------------------- /src/candybar.c: -------------------------------------------------------------------------------- 1 | #include "candybar.h" 2 | #include "widgets.h" 3 | 4 | static struct bar *bar = NULL; 5 | static pthread_mutex_t widget_thread_mutex = PTHREAD_MUTEX_INITIALIZER; 6 | 7 | static void 8 | parse_args (int argc, char *argv[], char **config_filename) { 9 | int opt; 10 | int int_arg; 11 | char *end; 12 | 13 | while ((opt = getopt(argc, argv, "c:h:m:p:t:d")) != -1) { 14 | switch (opt) { 15 | case 'c': 16 | *config_filename = optarg; 17 | break; 18 | case 'h': 19 | int_arg = strtol(optarg, &end, 10); 20 | if (*end) { 21 | LOG_ERR("invalid value for 'height': %s", optarg); 22 | exit(EXIT_FAILURE); 23 | } 24 | bar->height = int_arg; 25 | break; 26 | case 'p': 27 | bar->position = !strcmp(optarg, "bottom") 28 | ? BAR_POSITION_BOTTOM : BAR_POSITION_TOP; 29 | break; 30 | case 'm': 31 | int_arg = strtol(optarg, &end, 10); 32 | if (*end) { 33 | LOG_ERR("invalid value for 'monitor': %s", optarg); 34 | exit(EXIT_FAILURE); 35 | } 36 | bar->monitor = int_arg; 37 | break; 38 | case 't': 39 | bar->theme_uri = optarg; 40 | break; 41 | case 'd': 42 | bar->debug = true; 43 | break; 44 | } 45 | } 46 | } 47 | 48 | static gboolean 49 | wk_disable_context_menu_cb (WebKitWebView *web_view, GtkWidget *window) { 50 | /* Disable context menu */ 51 | return TRUE; 52 | } 53 | 54 | static void 55 | wk_realize_handler (GtkWidget *window, gpointer user_data) { 56 | GdkAtom net_wm_strut_atom = gdk_atom_intern_static_string("_NET_WM_STRUT"); 57 | GdkAtom net_wm_strut_partial_atom = gdk_atom_intern_static_string("_NET_WM_STRUT_PARTIAL"); 58 | GdkAtom cardinal_atom = gdk_atom_intern_static_string("CARDINAL"); 59 | GdkWindow *gdkw = gtk_widget_get_window(GTK_WIDGET(window)); 60 | gulong strut[4] = { 0 }; 61 | gulong strut_partial[12] = { 0 }; 62 | 63 | bool supports_net_wm_strut = g_list_find(gdk_get_net_supported(), 64 | net_wm_strut_atom) != NULL; 65 | bool supports_net_wm_strut_partial = g_list_find(gdk_get_net_supported(), 66 | net_wm_strut_partial_atom) != NULL; 67 | 68 | if (bar->position == BAR_POSITION_TOP) { 69 | strut[2] = bar->height; 70 | strut_partial[2] = bar->height; 71 | strut_partial[9] = bar->width; 72 | } 73 | else if (bar->position == BAR_POSITION_BOTTOM) { 74 | strut[3] = bar->height; 75 | strut_partial[3] = bar->height; 76 | strut_partial[11] = bar->width; 77 | } 78 | 79 | gdk_property_change(gdkw, net_wm_strut_atom, cardinal_atom, 80 | 32, GDK_PROP_MODE_REPLACE, (guchar*)strut, LENGTH(strut)); 81 | gdk_property_change(gdkw, net_wm_strut_partial_atom, cardinal_atom, 82 | 32, GDK_PROP_MODE_REPLACE, (guchar*)strut_partial, LENGTH(strut_partial)); 83 | 84 | if (!supports_net_wm_strut && !supports_net_wm_strut_partial) { 85 | /* only set override redirect if we're unable to reserve space 86 | with _NET_WM_STRUT or _NET_WM_STRUT_PARTIAL */ 87 | gdk_window_set_override_redirect(gdkw, TRUE); 88 | } 89 | } 90 | 91 | static WebKitWebView* 92 | wk_inspect_web_view_cb (WebKitWebInspector *web_inspector, 93 | WebKitWebView *web_view, 94 | gpointer user_data) { 95 | GtkWidget *window; 96 | GtkWidget *view; 97 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 98 | gtk_window_set_title(GTK_WINDOW(window), "Inspector"); 99 | view = webkit_web_view_new(); 100 | gtk_container_add(GTK_CONTAINER(window), view); 101 | g_object_set_data(G_OBJECT(web_view), "inspector-window", window); 102 | 103 | return WEBKIT_WEB_VIEW(view); 104 | } 105 | 106 | static gboolean 107 | wk_show_window_cb (WebKitWebInspector *web_inspector, GtkWidget *web_view) { 108 | GtkWidget *window; 109 | window = g_object_get_data(G_OBJECT(web_view), "inspector-window"); 110 | gtk_widget_show_all(window); 111 | 112 | return TRUE; 113 | } 114 | 115 | static void* 116 | inspector_init (WebKitWebView *web_view) { 117 | WebKitWebInspector *inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(web_view)); 118 | g_signal_connect(G_OBJECT(inspector), "inspect-web-view", G_CALLBACK(wk_inspect_web_view_cb), NULL); 119 | g_signal_connect(G_OBJECT(inspector), "show-window", G_CALLBACK(wk_show_window_cb), web_view); 120 | webkit_web_inspector_show(inspector); 121 | 122 | return 0; 123 | } 124 | 125 | static WebKitWebView* 126 | web_view_init () { 127 | WebKitWebView *web_view; 128 | WebKitWebSettings *web_view_settings; 129 | WebKitWebPluginDatabase *database; 130 | GSList *plugin_list, *p; 131 | WebKitWebPlugin *plugin; 132 | 133 | /* disable all plugins */ 134 | database = webkit_get_web_plugin_database(); 135 | plugin_list = webkit_web_plugin_database_get_plugins(database); 136 | for (p = plugin_list; p; p = p->next) { 137 | plugin = (WebKitWebPlugin*)p->data; 138 | webkit_web_plugin_set_enabled(plugin, FALSE); 139 | } 140 | webkit_web_plugin_database_refresh(database); 141 | webkit_web_plugin_database_plugins_list_free(plugin_list); 142 | 143 | /* set webview settings */ 144 | web_view_settings = webkit_web_settings_new(); 145 | g_object_set(G_OBJECT(web_view_settings), 146 | "enable-developer-extras", TRUE, 147 | "enable-accelerated-compositing", FALSE, 148 | "enable-dns-prefetching", FALSE, 149 | "enable-java-applet", FALSE, 150 | "enable-plugins", FALSE, 151 | "enable-universal-access-from-file-uris", TRUE, 152 | "enable-webgl", FALSE, 153 | "enable-xss-auditor", FALSE, 154 | NULL); 155 | 156 | web_view = WEBKIT_WEB_VIEW(webkit_web_view_new()); 157 | webkit_web_view_set_settings(web_view, web_view_settings); 158 | 159 | return web_view; 160 | } 161 | 162 | static void 163 | signal_handler (int signal) { 164 | if (pthread_mutex_trylock(&widget_thread_mutex) == 0) { 165 | if ((signal == SIGTERM) || (signal == SIGINT) || (signal == SIGHUP)) { 166 | join_widget_threads(bar); 167 | gtk_main_quit(); 168 | } 169 | if (signal == SIGUSR1) { 170 | join_widget_threads(bar); 171 | LOG_DEBUG("all widgets joined, reloading theme"); 172 | webkit_web_view_reload_bypass_cache(bar->web_view); 173 | pthread_mutex_unlock(&widget_thread_mutex); 174 | } 175 | } 176 | else { 177 | LOG_DEBUG("still waiting for widget threads!"); 178 | } 179 | } 180 | 181 | int 182 | main (int argc, char *argv[]) { 183 | char *config_filename = NULL; 184 | GtkWindow *window; 185 | GtkLayout *layout; 186 | GdkScreen *screen; 187 | int monitors_num; 188 | GdkRectangle dest; 189 | WebKitWebView *web_view; 190 | struct sigaction sa; 191 | 192 | gtk_init(&argc, &argv); 193 | 194 | LOG_INFO("%s%s%s %s (%s %s)", ANSI_ESC_CYAN, ANSI_ESC_BOLD, PACKAGE, VERSION, __DATE__, __TIME__); 195 | 196 | sa.sa_handler = signal_handler; 197 | sigemptyset(&sa.sa_mask); 198 | sa.sa_flags = 0; 199 | 200 | sigaction(SIGHUP, &sa, NULL); 201 | sigaction(SIGINT, &sa, NULL); 202 | sigaction(SIGTERM, &sa, NULL); 203 | sigaction(SIGUSR1, &sa, NULL); 204 | 205 | /* GtkScrolledWindow fails to lock small heights (<25px), so a GtkLayout 206 | is used instead */ 207 | window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); 208 | layout = GTK_LAYOUT(gtk_layout_new(NULL, NULL)); 209 | web_view = web_view_init(); 210 | 211 | /* init bar configuration */ 212 | bar = calloc(1, sizeof(struct bar)); 213 | bar->height = -1; /* default value */ 214 | bar->monitor = -1; /* default value */ 215 | bar->debug = false; /* default value */ 216 | bar->web_view = web_view; 217 | bar->efd = eventfd(0, 0); 218 | if (bar->efd == -1) { 219 | LOG_ERR("could not create event file descriptor: %s", strerror(errno)); 220 | goto config_err; 221 | } 222 | 223 | parse_args(argc, argv, &config_filename); 224 | 225 | if (bar->debug == true) { 226 | inspector_init(web_view); 227 | } 228 | 229 | bar->config = get_config_json(config_filename); 230 | if (!bar->config) { 231 | LOG_ERR("an error occured while loading the config file, aborting"); 232 | goto config_err; 233 | } 234 | 235 | if (bar->position == BAR_POSITION_UNKNOWN) { 236 | bar->position = !strcmp(get_config_option_string(bar->config, "position"), "bottom") 237 | ? BAR_POSITION_BOTTOM : BAR_POSITION_TOP; 238 | } 239 | if (bar->monitor == -1) { 240 | bar->monitor = get_config_option_integer(bar->config, "monitor"); 241 | } 242 | 243 | json_t *theme_config = get_config_option(bar->config, "theme", false); 244 | bar->theme_config = get_config_option(theme_config, "config", true); 245 | if (bar->theme_uri == NULL) { 246 | bar->theme_uri = get_config_option_string(theme_config, "uri"); 247 | } 248 | 249 | /* get window size */ 250 | screen = gtk_window_get_screen(window); 251 | monitors_num = gdk_screen_get_n_monitors(screen); 252 | if (monitors_num - 1 < bar->monitor) { 253 | LOG_ERR("invalid monitor index '%i'", bar->monitor); 254 | goto config_err; 255 | } 256 | gdk_screen_get_monitor_geometry(screen, bar->monitor, &dest); 257 | 258 | bar->width = dest.width; 259 | if (bar->height == -1) { 260 | bar->height = get_config_option_integer(bar->config, "height"); 261 | } 262 | 263 | /* set window properties */ 264 | gtk_window_set_default_size(window, bar->width, bar->height); 265 | gtk_window_stick(window); 266 | gtk_window_set_decorated(window, FALSE); 267 | gtk_window_set_skip_pager_hint(window, TRUE); 268 | gtk_window_set_skip_taskbar_hint(window, TRUE); 269 | gtk_window_set_gravity(window, GDK_GRAVITY_STATIC); 270 | gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_DOCK); 271 | gtk_widget_set_size_request(GTK_WIDGET(web_view), bar->width, bar->height); 272 | 273 | gtk_container_add(GTK_CONTAINER(layout), GTK_WIDGET(web_view)); 274 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(layout)); 275 | 276 | if (bar->position == BAR_POSITION_TOP) { 277 | bar->pos_x = dest.x; 278 | bar->pos_y = dest.y; 279 | gtk_window_move(window, bar->pos_x, bar->pos_y); 280 | } 281 | else if (bar->position == BAR_POSITION_BOTTOM) { 282 | bar->pos_x = dest.x; 283 | bar->pos_y = dest.y + dest.height - bar->height; 284 | gtk_window_move(window, bar->pos_x, bar->pos_y); 285 | } 286 | 287 | if (!bar->debug) { 288 | g_signal_connect(web_view, "context-menu", G_CALLBACK(wk_disable_context_menu_cb), NULL); 289 | } 290 | 291 | g_signal_connect(web_view, "window-object-cleared", G_CALLBACK(wk_window_object_cleared_cb), bar); 292 | g_signal_connect(web_view, "notify::load-status", G_CALLBACK(wk_load_status_cb), NULL); 293 | g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); 294 | g_signal_connect(window, "realize", G_CALLBACK(wk_realize_handler), bar); 295 | 296 | LOG_INFO("loading theme '%s'", bar->theme_uri); 297 | webkit_web_view_load_uri(web_view, bar->theme_uri); 298 | 299 | gtk_widget_show_all(GTK_WIDGET(window)); 300 | 301 | gtk_main(); 302 | 303 | json_decref(bar->config); 304 | json_decref(theme_config); 305 | config_err: 306 | free(bar); 307 | 308 | return 0; 309 | } 310 | -------------------------------------------------------------------------------- /src/candybar.h: -------------------------------------------------------------------------------- 1 | #ifndef CANDYBAR_H 2 | #define CANDYBAR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "util/config.h" 14 | #include "util/copy_prop.h" 15 | #include "util/gdk_helpers.h" 16 | #include "util/log.h" 17 | 18 | typedef enum { 19 | BAR_POSITION_UNKNOWN, 20 | BAR_POSITION_TOP, 21 | BAR_POSITION_BOTTOM, 22 | } bar_position_t; 23 | 24 | struct bar { 25 | WebKitWebView *web_view; 26 | bar_position_t position; 27 | int pos_x; 28 | int pos_y; 29 | int width; 30 | int height; 31 | int monitor; 32 | json_t *config; 33 | const char *theme_uri; 34 | json_t *theme_config; 35 | int efd; 36 | bool debug; 37 | }; 38 | 39 | #define MISSING_VALUE "" 40 | #define LENGTH(X) (sizeof X / sizeof X[0]) 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "height": 24, 3 | "position": "top", 4 | "monitor": 0, 5 | "theme": { 6 | "uri": "@THEME_URI@", 7 | "config": {} 8 | }, 9 | "widgets": [ 10 | { 11 | "module": "desktops", 12 | "config": {} 13 | }, 14 | { 15 | "module": "window_title", 16 | "config": {} 17 | }, 18 | { 19 | "module": "now_playing_mpd", 20 | "config": {} 21 | }, 22 | { 23 | "module": "volume", 24 | "config": {} 25 | }, 26 | { 27 | "module": "weather", 28 | "config": {} 29 | }, 30 | { 31 | "module": "external_ip", 32 | "config": {} 33 | }, 34 | { 35 | "module": "battery", 36 | "config": {} 37 | }, 38 | { 39 | "module": "datetime", 40 | "config": {} 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /src/util/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | json_t* 4 | get_config_json (char *config_override_filename) { 5 | const gchar*const *paths_array; 6 | gchar *config_filename; 7 | gchar *override_path; 8 | json_t *json_config; 9 | json_error_t err; 10 | 11 | override_path = config_override_filename != NULL 12 | ? config_override_filename : getenv("CANDYBAR_CONFIG_PATH"); 13 | if (override_path != NULL) { 14 | config_filename = g_build_filename(override_path, NULL); 15 | 16 | if (access(config_filename, R_OK) == -1) { 17 | LOG_ERR("could not open config file '%s' for reading: %s", config_filename, strerror(errno)); 18 | 19 | return NULL; 20 | } 21 | } 22 | else { 23 | config_filename = g_build_filename(g_get_user_config_dir(), PACKAGE, "config.json", NULL); 24 | } 25 | 26 | LOG_INFO("using config file '%s'", config_filename); 27 | 28 | json_config = json_load_file(config_filename, 0, &err); 29 | if (!json_config) { 30 | if (err.line != -1) { 31 | /* syntax error */ 32 | LOG_ERR("error in config file: %s", err.text); 33 | 34 | return NULL; 35 | } 36 | else { 37 | /* file not found 38 | go through all the paths in system_config_dirs */ 39 | for (paths_array = g_get_system_config_dirs(); *paths_array; paths_array++) { 40 | g_free(config_filename); 41 | config_filename = g_build_filename(*paths_array, PACKAGE, "config.json", NULL); 42 | json_config = json_load_file(config_filename, 0, &err); 43 | if (!json_config) { 44 | if (err.line != -1) { 45 | /* syntax error */ 46 | LOG_ERR("error in config file: %s", err.text); 47 | 48 | return NULL; 49 | } 50 | } 51 | else { 52 | /* this file is good */ 53 | break; 54 | } 55 | } 56 | } 57 | } 58 | g_free(config_filename); 59 | 60 | return json_config; 61 | } 62 | 63 | json_t* 64 | get_config_option (json_t *config_object, const char *option, bool silent) { 65 | json_t *value = json_object_get(config_object, option); 66 | if (!value && !silent) { 67 | LOG_WARN("option '%s' not found in config object", option); 68 | } 69 | 70 | return value; 71 | } 72 | -------------------------------------------------------------------------------- /src/util/config.h: -------------------------------------------------------------------------------- 1 | #include "candybar.h" 2 | 3 | json_t* get_config_json (char *config_override_filename); 4 | json_t* get_config_option (json_t *config_object, const char *key, bool silent); 5 | 6 | #define get_config_option_string(OBJ, OPTION) json_string_value(get_config_option(OBJ, OPTION, false)) 7 | #define get_config_option_integer(OBJ, OPTION) json_integer_value(get_config_option(OBJ, OPTION, false)) 8 | #define get_config_option_real(OBJ, OPTION) json_real_value(get_config_option(OBJ, OPTION, false)) 9 | #define get_config_option_boolean(OBJ, OPTION) json_is_true(get_config_option(OBJ, OPTION, false)) 10 | 11 | #define get_config_option_string_silent(OBJ, OPTION) json_string_value(get_config_option(OBJ, OPTION, true)) 12 | #define get_config_option_integer_silent(OBJ, OPTION) json_integer_value(get_config_option(OBJ, OPTION, true)) 13 | #define get_config_option_real_silent(OBJ, OPTION) json_real_value(get_config_option(OBJ, OPTION, true)) 14 | #define get_config_option_boolean_silent(OBJ, OPTION) json_is_true(get_config_option(OBJ, OPTION, true)) 15 | -------------------------------------------------------------------------------- /src/util/copy_prop.c: -------------------------------------------------------------------------------- 1 | #include "copy_prop.h" 2 | 3 | void 4 | copy_prop (char *dest, char *src, int len, int idx, int num_itm) { 5 | if (num_itm <= 1) { 6 | strncpy(dest, src, MIN(len, COPY_PROP_BUFSIZ)); 7 | dest[len] = '\0'; 8 | } 9 | else { 10 | int pos = 0, cnt = 0; 11 | while (cnt < idx && cnt < (num_itm - 1) && pos < len) { 12 | pos += strlen(src + pos) + 1; 13 | cnt++; 14 | } 15 | if (cnt == (num_itm - 1)) { 16 | copy_prop(dest, src + pos, len - pos, 0, 1); 17 | } 18 | else { 19 | strncpy(dest, src + pos, COPY_PROP_BUFSIZ); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/util/copy_prop.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define COPY_PROP_BUFSIZ 512 4 | #ifndef MIN 5 | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) 6 | #endif 7 | 8 | void copy_prop (char *dest, char *src, int len, int idx, int num_itm); 9 | -------------------------------------------------------------------------------- /src/util/curl.c: -------------------------------------------------------------------------------- 1 | #include "curl.h" 2 | 3 | size_t 4 | candybar_curl_write_response (void *ptr, size_t size, size_t nmemb, void *stream) { 5 | write_result_t *result = (write_result_t*)stream; 6 | 7 | if (result->pos + size * nmemb >= CURL_BUF_SIZE - 1) { 8 | LOG_INFO("curl: too small buffer"); 9 | 10 | return 0; 11 | } 12 | 13 | memcpy(result->data + result->pos, ptr, size * nmemb); 14 | result->pos += size * nmemb; 15 | 16 | return size * nmemb; 17 | } 18 | 19 | char* 20 | candybar_curl_request (const char *url) { 21 | CURL *curl; 22 | CURLcode status; 23 | char *data; 24 | long code; 25 | 26 | curl = curl_easy_init(); 27 | data = malloc(CURL_BUF_SIZE); 28 | if (!curl || !data) { 29 | return NULL; 30 | } 31 | 32 | write_result_t write_result = { 33 | .data = data, 34 | .pos = 0 35 | }; 36 | 37 | curl_easy_setopt(curl, CURLOPT_URL, url); 38 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, candybar_curl_write_response); 39 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); 40 | 41 | status = curl_easy_perform(curl); 42 | if (status != 0) { 43 | LOG_ERR("curl: unable to request data from %s:\n%s", url, curl_easy_strerror(status)); 44 | 45 | return NULL; 46 | } 47 | 48 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); 49 | if (code != 200) { 50 | LOG_ERR("curl: server responded with code %ld", code); 51 | 52 | return NULL; 53 | } 54 | 55 | curl_easy_cleanup(curl); 56 | curl_global_cleanup(); 57 | 58 | data[write_result.pos] = '\0'; 59 | 60 | return data; 61 | } 62 | -------------------------------------------------------------------------------- /src/util/curl.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "util/log.h" 6 | 7 | #define CURL_BUF_SIZE (256 * 1024) 8 | 9 | typedef struct write_result_t { 10 | char *data; 11 | int pos; 12 | } write_result_t; 13 | 14 | size_t candybar_curl_write_response (void *ptr, size_t size, size_t nmemb, void *stream); 15 | char* candybar_curl_request (const char *url); 16 | -------------------------------------------------------------------------------- /src/util/dbus_helpers.c: -------------------------------------------------------------------------------- 1 | #include "dbus_helpers.h" 2 | 3 | static gboolean 4 | proxy_property_value (DBusGProxy *properties_proxy, char *path, char *property, GValue *get_value, GError **error) { 5 | return dbus_g_proxy_call(properties_proxy, "Get", error, 6 | G_TYPE_STRING, path, 7 | G_TYPE_STRING, property, 8 | G_TYPE_INVALID, 9 | G_TYPE_VALUE, get_value, 10 | G_TYPE_INVALID); 11 | } 12 | 13 | gboolean 14 | proxy_double_value (gdouble *value, DBusGProxy *properties_proxy, char *path, char *property) { 15 | GError *error = NULL; 16 | GValue get_value = { 0, }; 17 | 18 | if (!proxy_property_value(properties_proxy, path, property, &get_value, &error)) { 19 | LOG_ERR("dbus error: %s", error->message); 20 | g_error_free(error); 21 | 22 | return FALSE; 23 | } 24 | 25 | *value = g_value_get_double(&get_value); 26 | g_value_unset(&get_value); 27 | 28 | return TRUE; 29 | } 30 | 31 | gboolean 32 | proxy_uint64_value (guint64 *value, DBusGProxy *properties_proxy, char *path, char *property) { 33 | GError *error = NULL; 34 | GValue get_value = { 0, }; 35 | 36 | if (!proxy_property_value(properties_proxy, path, property, &get_value, &error)) { 37 | LOG_ERR("dbus error: %s", error->message); 38 | g_error_free(error); 39 | 40 | return FALSE; 41 | } 42 | 43 | *value = g_value_get_uint64(&get_value); 44 | g_value_unset(&get_value); 45 | 46 | return TRUE; 47 | } 48 | 49 | gboolean 50 | proxy_uint_value (guint *value, DBusGProxy *properties_proxy, char *path, char *property) { 51 | GError *error = NULL; 52 | GValue get_value = { 0, }; 53 | 54 | if (!proxy_property_value(properties_proxy, path, property, &get_value, &error)) { 55 | LOG_ERR("dbus error: %s", error->message); 56 | g_error_free(error); 57 | 58 | return FALSE; 59 | } 60 | 61 | *value = g_value_get_uint(&get_value); 62 | g_value_unset(&get_value); 63 | 64 | return TRUE; 65 | } 66 | 67 | gboolean 68 | proxy_int64_value (gint64 *value, DBusGProxy *properties_proxy, char *path, char *property) { 69 | GError *error = NULL; 70 | GValue get_value = { 0, }; 71 | 72 | if (!proxy_property_value(properties_proxy, path, property, &get_value, &error)) { 73 | LOG_ERR("dbus error: %s", error->message); 74 | g_error_free(error); 75 | 76 | return FALSE; 77 | } 78 | 79 | *value = g_value_get_int64(&get_value); 80 | g_value_unset(&get_value); 81 | 82 | return TRUE; 83 | } 84 | 85 | gboolean 86 | proxy_int_value (gint *value, DBusGProxy *properties_proxy, char *path, char *property) { 87 | GError *error = NULL; 88 | GValue get_value = { 0, }; 89 | 90 | if (!proxy_property_value(properties_proxy, path, property, &get_value, &error)) { 91 | LOG_ERR("dbus error: %s", error->message); 92 | g_error_free(error); 93 | 94 | return FALSE; 95 | } 96 | 97 | *value = g_value_get_int(&get_value); 98 | g_value_unset(&get_value); 99 | 100 | return TRUE; 101 | } 102 | -------------------------------------------------------------------------------- /src/util/dbus_helpers.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util/log.h" 5 | 6 | gboolean proxy_double_value (gdouble *value, DBusGProxy *properties_proxy, char *path, char *property); 7 | gboolean proxy_uint64_value (guint64 *value, DBusGProxy *properties_proxy, char *path, char *property); 8 | gboolean proxy_uint_value (guint *value, DBusGProxy *properties_proxy, char *path, char *property); 9 | gboolean proxy_int64_value (gint64 *value, DBusGProxy *properties_proxy, char *path, char *property); 10 | gboolean proxy_int_value (gint *value, DBusGProxy *properties_proxy, char *path, char *property); 11 | -------------------------------------------------------------------------------- /src/util/gdk_helpers.c: -------------------------------------------------------------------------------- 1 | #include "util/log.h" 2 | #include "gdk_helpers.h" 3 | 4 | GList* 5 | gdk_get_net_supported () { 6 | GdkAtom actual_property_type; 7 | int actual_format; 8 | int actual_length; 9 | long *data = NULL; 10 | GList *list = NULL; 11 | unsigned short i; 12 | 13 | if (!gdk_property_get(gdk_screen_get_root_window(gdk_screen_get_default()), 14 | gdk_atom_intern("_NET_SUPPORTED", FALSE), 15 | gdk_atom_intern("ATOM", FALSE), 16 | 0, 17 | G_MAXLONG, 18 | FALSE, 19 | &actual_property_type, 20 | &actual_format, 21 | &actual_length, 22 | (guchar**)&data)) { 23 | gchar *actual_property_type_name; 24 | LOG_ERR("unable to get _NET_SUPPORTED"); 25 | actual_property_type_name = gdk_atom_name(actual_property_type); 26 | if (actual_property_type_name) { 27 | LOG_INFO("actual_property_type: %s", actual_property_type_name); 28 | g_free(actual_property_type_name); 29 | } 30 | 31 | return NULL; 32 | } 33 | 34 | /* Put all of the GdkAtoms into a GList to return */ 35 | for (i = 0; i < actual_length / sizeof(long); i++) { 36 | list = g_list_append(list, (GdkAtom)data[i]); 37 | } 38 | 39 | g_free(data); 40 | 41 | return list; 42 | } 43 | -------------------------------------------------------------------------------- /src/util/gdk_helpers.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | GList* gdk_get_net_supported (); 4 | -------------------------------------------------------------------------------- /src/util/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; 4 | 5 | void 6 | LOG (const char *prefix, const char *color, const char *func, char *file, const char *format, ...) { 7 | va_list args; 8 | time_t rawtime; 9 | struct tm *date; 10 | 11 | pthread_mutex_lock(&log_mutex); 12 | 13 | time(&rawtime); 14 | date = localtime(&rawtime); 15 | fprintf(stderr, "%s%s[%02d:%02d:%02d] %s%s%s %s%s", 16 | ANSI_ESC_RESET, ANSI_ESC_WHITE, 17 | date->tm_hour, date->tm_min, date->tm_sec, 18 | ANSI_ESC_BOLD, color, 19 | prefix, 20 | ANSI_ESC_RESET, color); 21 | va_start(args, format); 22 | vfprintf(stderr, format, args); 23 | va_end(args); 24 | if (strstr(file, "widgets/")) { 25 | fprintf(stderr, " %s%s(%s)", 26 | ANSI_ESC_RESET, ANSI_ESC_WHITE, 27 | basename(file)); 28 | } 29 | fprintf(stderr, ANSI_ESC_RESET "\n"); 30 | 31 | pthread_mutex_unlock(&log_mutex); 32 | } 33 | -------------------------------------------------------------------------------- /src/util/log.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define ANSI_ESC_RED "\x1b[31m" 9 | #define ANSI_ESC_GREEN "\x1b[32m" 10 | #define ANSI_ESC_YELLOW "\x1b[33m" 11 | #define ANSI_ESC_BLUE "\x1b[34m" 12 | #define ANSI_ESC_MAGENTA "\x1b[35m" 13 | #define ANSI_ESC_CYAN "\x1b[36m" 14 | #define ANSI_ESC_WHITE "\x1b[37m" 15 | #define ANSI_ESC_BOLD "\x1b[1m" 16 | #define ANSI_ESC_RESET "\x1b[0m" 17 | 18 | void LOG (const char *prefix, const char *color, const char *func, char *file, const char *format, ...); 19 | 20 | #define LOG_ERR(...) LOG("ERROR", ANSI_ESC_RED, __func__, __FILE__, __VA_ARGS__) 21 | #define LOG_INFO(...) LOG("INFO", ANSI_ESC_RESET, __func__, __FILE__, __VA_ARGS__) 22 | #define LOG_WARN(...) LOG("WARNING", ANSI_ESC_YELLOW, __func__, __FILE__, __VA_ARGS__) 23 | 24 | #ifdef DEBUG 25 | #define LOG_DEBUG(...) LOG("DEBUG", ANSI_ESC_WHITE, __func__, __FILE__, __VA_ARGS__) 26 | #else 27 | #define LOG_DEBUG(...) 28 | #endif 29 | -------------------------------------------------------------------------------- /src/util/process.c: -------------------------------------------------------------------------------- 1 | #include "process.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define EINTRWRAP(VAR, BLOCK) \ 13 | do { \ 14 | VAR = BLOCK; \ 15 | } while (VAR == -1 && errno == EINTR) 16 | 17 | static inline void 18 | append_flag (int fd, int flag) { 19 | int err; 20 | EINTRWRAP(err, fcntl(fd, F_GETFL, 0)); 21 | assert(err >= 0); 22 | const int flags = err | flag; 23 | EINTRWRAP(err, fcntl(fd, F_SETFL, flags)); 24 | assert(err >= 0); 25 | } 26 | 27 | static inline void 28 | close_fd (int *fd) { 29 | if (*fd != -1) { 30 | int err; 31 | EINTRWRAP(err, close(*fd)); 32 | *fd = -1; 33 | } 34 | } 35 | 36 | static inline int 37 | read_fd (int *fd, struct Process *proc, WriteCallback cb) { 38 | char buf[BUFSIZ]; 39 | int ret; 40 | EINTRWRAP(ret, read(*fd, buf, sizeof(buf))); 41 | if (!ret) { 42 | close_fd(fd); 43 | } 44 | else if (( ret > 0) && cb) { 45 | cb(proc, buf, ret); 46 | } 47 | 48 | return ret; 49 | } 50 | 51 | int 52 | process (struct Process *proc) { 53 | assert(proc); 54 | assert(proc->path); 55 | assert(proc->argv); 56 | memset(proc->error, 0, sizeof(proc->error)); 57 | int stdin[2], stdout[2], stderr[2], close_pipe[2]; 58 | int err; 59 | EINTRWRAP(err, pipe(stdin)); 60 | assert(!err); 61 | EINTRWRAP(err, pipe(stdout)); 62 | assert(!err); 63 | EINTRWRAP(err, pipe(stderr)); 64 | assert(!err); 65 | EINTRWRAP(err, pipe(close_pipe)); 66 | assert(!err); 67 | 68 | append_flag(close_pipe[1], FD_CLOEXEC); 69 | 70 | const int pid = fork(); 71 | 72 | if (pid == -1) { 73 | EINTRWRAP(err, close(close_pipe[1])); 74 | EINTRWRAP(err, close(close_pipe[0])); 75 | EINTRWRAP(err, close(stdin[1])); 76 | EINTRWRAP(err, close(stdin[0])); 77 | EINTRWRAP(err, close(stdout[1])); 78 | EINTRWRAP(err, close(stdout[0])); 79 | EINTRWRAP(err, close(stderr[1])); 80 | EINTRWRAP(err, close(stderr[0])); 81 | snprintf(proc->error, sizeof(proc->error), "Fork failed %d", errno); 82 | 83 | return -1; 84 | } 85 | else if (pid == 0) { 86 | EINTRWRAP(err, close(close_pipe[0])); 87 | EINTRWRAP(err, close(stdin[1])); 88 | EINTRWRAP(err, close(stdin[1])); 89 | EINTRWRAP(err, close(stdout[0])); 90 | EINTRWRAP(err, close(stderr[0])); 91 | 92 | EINTRWRAP(err, dup2(stdin[0], STDIN_FILENO)); 93 | EINTRWRAP(err, close(stdin[0])); 94 | EINTRWRAP(err, dup2(stdout[1], STDOUT_FILENO)); 95 | EINTRWRAP(err, close(stdout[1])); 96 | EINTRWRAP(err, dup2(stderr[1], STDERR_FILENO)); 97 | EINTRWRAP(err, close(stderr[1])); 98 | 99 | if (proc->cwd && strlen(proc->cwd)) { 100 | EINTRWRAP(err, chdir(proc->cwd)); 101 | } 102 | const int ret = execv(proc->path, (char*const*)proc->argv); 103 | 104 | /* notify the parent process */ 105 | const char c = 'c'; 106 | EINTRWRAP(err, write(close_pipe[1], &c, 1)); 107 | EINTRWRAP(err, close(close_pipe[1])); 108 | _exit(1); 109 | (void)ret; 110 | } 111 | else { 112 | /* parent */ 113 | EINTRWRAP(err, close(close_pipe[1])); 114 | EINTRWRAP(err, close(stdin[0])); 115 | EINTRWRAP(err, close(stdout[1])); 116 | EINTRWRAP(err, close(stderr[1])); 117 | 118 | /* printf("fork, in parent\n"); */ 119 | 120 | append_flag(stdin[1], O_NONBLOCK); 121 | 122 | int status = 0, max, closed = 0; 123 | size_t stdin_written = 0; 124 | while (stdin_written < proc->stdin_length) { 125 | EINTRWRAP(err, write(stdin[1], proc->stdin_buffer + stdin_written, proc->stdin_length - stdin_written)); 126 | if (err > 0) { 127 | stdin_written += err; 128 | } 129 | else if (err < 0) { 130 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { 131 | break; 132 | } 133 | else { 134 | status = -1; 135 | goto cleanup; 136 | } 137 | } 138 | } 139 | assert(stdin_written <= proc->stdin_length); 140 | if (stdin_written == proc->stdin_length) { 141 | close_fd(&stdin[1]); 142 | ++closed; 143 | } 144 | 145 | max = close_pipe[0]; 146 | if (stdout[0] > max) { 147 | max = stdout[0]; 148 | } 149 | if (stderr[0] > max) { 150 | max = stderr[0]; 151 | } 152 | if (stdin[1] > max) { 153 | max = stdin[1]; 154 | } 155 | 156 | do { 157 | fd_set r, w; 158 | FD_ZERO(&r); 159 | FD_ZERO(&w); 160 | if (close_pipe[0] != -1) { 161 | FD_SET(close_pipe[0], &r); 162 | } 163 | if (stdout[0] != -1) { 164 | FD_SET(stdout[0], &r); 165 | } 166 | if (stderr[0] != -1) { 167 | FD_SET(stderr[0], &r); 168 | } 169 | if (stdin[1] != -1) { 170 | FD_SET(stdin[1], &w); 171 | } 172 | 173 | const int ret = select(max + 1, &r, &w, 0, 0); 174 | assert(ret); 175 | if (ret > 0) { 176 | if ((stdout[0] != -1) && FD_ISSET(stdout[0], &r)) { 177 | const int ret = read_fd(&stdout[0], proc, proc->stdout_cb); 178 | if (!ret) { 179 | ++closed; 180 | } 181 | else if (ret < 0) { 182 | snprintf(proc->error, sizeof(proc->error), "read failed for process %s stdout %d", proc->path, errno); 183 | status = -1; 184 | goto cleanup; 185 | } 186 | } 187 | if ((stderr[0] != -1) && FD_ISSET(stderr[0], &r)) { 188 | const int ret = read_fd(&stderr[0], proc, proc->stderr_cb); 189 | if (!ret) { 190 | ++closed; 191 | } 192 | else if (ret < 0) { 193 | snprintf(proc->error, sizeof(proc->error), "read failed for process %s stderr %d", proc->path, errno); 194 | status = -1; 195 | goto cleanup; 196 | } 197 | } 198 | if ((close_pipe[0] != -1) && FD_ISSET(close_pipe[0], &r)) { 199 | const int ret = read_fd(&close_pipe[0], 0, 0); 200 | if (ret > 0) { 201 | snprintf(proc->error, sizeof(proc->error), "exec failed process %s %d", proc->path, errno); 202 | status = -1; 203 | goto cleanup; 204 | } 205 | else if (ret < 0) { 206 | snprintf(proc->error, sizeof(proc->error), "read failed for process %s close pipe %d", proc->path, errno); 207 | status = -1; 208 | goto cleanup; 209 | } 210 | } 211 | 212 | if ((stdin[1] != -1) && FD_ISSET(stdin[1], &w)) { 213 | while (stdin_written < proc->stdin_length) { 214 | EINTRWRAP(err, write(stdin[1], proc->stdin_buffer + stdin_written, proc->stdin_length - stdin_written)); 215 | if (err > 0) { 216 | stdin_written += err; 217 | } 218 | else if (err < 0) { 219 | if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { 220 | break; 221 | } 222 | else { 223 | status = -1; 224 | goto cleanup; 225 | } 226 | } 227 | } 228 | assert(stdin_written <= proc->stdin_length); 229 | if (stdin_written == proc->stdin_length) { 230 | close_fd(&stdin[1]); 231 | ++closed; 232 | } 233 | } 234 | } 235 | else if (errno != EINTR) { 236 | snprintf(proc->error, sizeof(proc->error), "select failed %d", errno); 237 | status = -1; 238 | break; 239 | } 240 | } while (closed < 3); 241 | cleanup: 242 | { 243 | int status_code; 244 | EINTRWRAP(err, waitpid(pid, &status_code, 0)); 245 | if (status != -1) { 246 | status = status_code; 247 | } 248 | close_fd(&close_pipe[0]); 249 | close_fd(&stdin[1]); 250 | close_fd(&stdout[0]); 251 | close_fd(&stderr[0]); 252 | } 253 | 254 | return status; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/util/process.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_H 2 | #define PROCESS_H 3 | 4 | #include 5 | 6 | struct Process; 7 | typedef void (*WriteCallback)(struct Process *proc, const char*, size_t); 8 | 9 | struct Process { 10 | WriteCallback stderr_cb, stdout_cb; 11 | char *stdin_buffer; 12 | size_t stdin_length; 13 | char error[1024]; 14 | void *user_data; 15 | 16 | char *path; 17 | char *cwd; 18 | char **argv; 19 | }; 20 | 21 | int process (struct Process *proc); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/widgets.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "candybar.h" 3 | 4 | static struct widget **widgets_active = NULL; 5 | static size_t widgets_len = 0; 6 | 7 | pthread_mutex_t web_view_ready_mutex = PTHREAD_MUTEX_INITIALIZER; 8 | pthread_mutex_t update_mutex = PTHREAD_MUTEX_INITIALIZER; 9 | pthread_cond_t update_cond = PTHREAD_COND_INITIALIZER; 10 | 11 | static void 12 | init_widget_js_obj (void *context, struct widget *widget) { 13 | char classname[64]; 14 | snprintf(classname, 64, "widget_%s", widget->type); 15 | const JSClassDefinition widget_js_def = { 16 | .className = classname, 17 | .staticFunctions = widget->js_staticfuncs, 18 | }; 19 | 20 | JSClassRef class_def = JSClassCreate(&widget_js_def); 21 | JSObjectRef class_obj = JSObjectMake(context, class_def, widget); 22 | JSObjectRef global_obj = JSContextGetGlobalObject(context); 23 | JSStringRef str_name = JSStringCreateWithUTF8CString(classname); 24 | JSObjectSetProperty(context, global_obj, str_name, class_obj, kJSPropertyAttributeNone, NULL); 25 | JSStringRelease(str_name); 26 | 27 | widget->js_context = context; 28 | widget->js_object = class_obj; 29 | } 30 | 31 | static struct widget* 32 | spawn_widget (struct bar *bar, void *context, json_t *config, const char *name) { 33 | widget_main_t widget_main; 34 | widget_type_t widget_type; 35 | char libname[64]; 36 | snprintf(libname, 64, "libwidget_%s", name); 37 | gchar *libpath = g_module_build_path(LIBDIR, libname); 38 | GModule *lib = g_module_open(libpath, G_MODULE_BIND_LOCAL); 39 | pthread_t return_thread; 40 | struct widget *widget = calloc(1, sizeof(struct widget)); 41 | 42 | if (lib == NULL) { 43 | LOG_WARN("loading of '%s' (%s) failed", libpath, name); 44 | 45 | goto error; 46 | } 47 | 48 | if (!g_module_symbol(lib, "widget_main", (void*)&widget_main)) { 49 | LOG_WARN("loading of '%s' (%s) failed: unable to load module symbol", libpath, name); 50 | 51 | goto error; 52 | } 53 | 54 | JSStaticFunction *js_staticfuncs = calloc(1, sizeof(JSStaticFunction)); 55 | if (g_module_symbol(lib, "widget_js_staticfuncs", (void*)&js_staticfuncs)) { 56 | widget->js_staticfuncs = js_staticfuncs; 57 | } 58 | else { 59 | free(js_staticfuncs); 60 | } 61 | 62 | widget->bar = bar; 63 | widget->config = config; 64 | widget->name = strdup(name); 65 | 66 | pthread_mutex_init(&widget->exit_mutex, NULL); 67 | pthread_cond_init(&widget->exit_cond, NULL); 68 | 69 | if (g_module_symbol(lib, "widget_type", (void*)&widget_type)) { 70 | widget->type = widget_type(); 71 | } 72 | else { 73 | widget->type = widget->name; 74 | } 75 | 76 | init_widget_js_obj(context, widget); 77 | 78 | if (pthread_create(&return_thread, NULL, (void*(*)(void*))widget_main, widget) != 0) { 79 | LOG_ERR("failed to start widget %s: %s", name, strerror(errno)); 80 | 81 | goto error; 82 | } 83 | 84 | widget->thread = return_thread; 85 | 86 | return widget; 87 | 88 | error: 89 | if (widget->name != NULL) { 90 | free(widget->name); 91 | } 92 | if (widget->js_staticfuncs != NULL) { 93 | free(widget->js_staticfuncs); 94 | } 95 | free(widget); 96 | 97 | return 0; 98 | } 99 | 100 | void 101 | join_widget_threads (struct bar *bar) { 102 | unsigned short i; 103 | struct timespec timeout; 104 | 105 | if (widgets_active && (widgets_len > 0)) { 106 | LOG_DEBUG("gracefully shutting down widget threads..."); 107 | for (i = 0; i < widgets_len; i++) { 108 | /* make all threads wait until we're ready to receive 109 | the cond signal below */ 110 | pthread_mutex_lock(&widgets_active[i]->exit_mutex); 111 | } 112 | 113 | /* send exit signal */ 114 | eventfd_write(bar->efd, 1); 115 | for (i = 0; i < widgets_len; i++) { 116 | /* update cond timeout */ 117 | clock_gettime(CLOCK_REALTIME, &timeout); 118 | timeout.tv_sec += 2; 119 | 120 | /* wait until thread times out or sends an exit 121 | confirmation signal */ 122 | int ret = pthread_cond_timedwait(&widgets_active[i]->exit_cond, &widgets_active[i]->exit_mutex, &timeout); 123 | 124 | if (ret == ETIMEDOUT) { 125 | LOG_WARN("timed out waiting for widget %s to exit", widgets_active[i]->name); 126 | pthread_cancel(widgets_active[i]->thread); 127 | } 128 | else { 129 | pthread_join(widgets_active[i]->thread, NULL); 130 | } 131 | } 132 | 133 | /* read any data from the efd so it blocks on epoll_wait */ 134 | eventfd_read(bar->efd, NULL); 135 | free(widgets_active); 136 | } 137 | else { 138 | LOG_DEBUG("no widget threads have been spawned"); 139 | } 140 | } 141 | 142 | bool 143 | web_view_callback (struct js_callback_data *data) { 144 | unsigned short i; 145 | 146 | JSValueRef js_args[data->args_len]; 147 | for (i = 0; i < data->args_len; i++) { 148 | switch (data->args[i].type) { 149 | case kJSTypeBoolean: 150 | js_args[i] = JSValueMakeBoolean(data->widget->js_context, data->args[i].value.boolean); 151 | break; 152 | case kJSTypeNull: 153 | js_args[i] = JSValueMakeNull(data->widget->js_context); 154 | break; 155 | case kJSTypeNumber: 156 | js_args[i] = JSValueMakeNumber(data->widget->js_context, data->args[i].value.number); 157 | break; 158 | case kJSTypeObject: 159 | js_args[i] = data->args[i].value.object; 160 | break; 161 | case kJSTypeString: { 162 | JSStringRef str = JSStringCreateWithUTF8CString(data->args[i].value.string); 163 | js_args[i] = JSValueMakeString(data->widget->js_context, str); 164 | JSStringRelease(str); 165 | break; 166 | } 167 | case kJSTypeUndefined: 168 | js_args[i] = JSValueMakeUndefined(data->widget->js_context); 169 | break; 170 | } 171 | } 172 | 173 | if (!data->widget->js_context || !data->widget->js_object) { 174 | LOG_ERR("missing JS context or object!"); 175 | 176 | return false; 177 | } 178 | 179 | JSStringRef str_ondatachanged = JSStringCreateWithUTF8CString("onDataChanged"); 180 | JSValueRef func = JSObjectGetProperty(data->widget->js_context, data->widget->js_object, str_ondatachanged, NULL); 181 | JSObjectRef function = JSValueToObject(data->widget->js_context, func, NULL); 182 | JSStringRelease(str_ondatachanged); 183 | 184 | /* let the thread know we're done with the data so it can cleanup */ 185 | pthread_cond_signal(&update_cond); 186 | 187 | if (!JSObjectIsFunction(data->widget->js_context, function)) { 188 | LOG_DEBUG("onDataChanged callback for 'widget_%s' with type '%s' is not a function or is not set", data->widget->name, data->widget->type); 189 | 190 | return false; /* only run once */ 191 | } 192 | 193 | JSObjectCallAsFunction(data->widget->js_context, function, NULL, data->args_len, js_args, NULL); 194 | 195 | return false; /* only run once */ 196 | } 197 | 198 | void 199 | wk_load_status_cb (GObject *object, GParamSpec *pspec, gpointer data) { 200 | WebKitWebView *web_view = WEBKIT_WEB_VIEW(object); 201 | WebKitLoadStatus status = webkit_web_view_get_load_status(web_view); 202 | 203 | if (status == WEBKIT_LOAD_FINISHED) { 204 | LOG_DEBUG("webkit: load finished"); 205 | pthread_mutex_unlock(&web_view_ready_mutex); 206 | } 207 | } 208 | 209 | void 210 | wk_window_object_cleared_cb (WebKitWebView *web_view, GParamSpec *pspec, void *context, void *window_object, void *user_data) { 211 | LOG_DEBUG("webkit: window object cleared"); 212 | struct bar *bar = user_data; 213 | 214 | json_t *widget; 215 | json_t *widgets = json_object_get(bar->config, "widgets"); 216 | size_t index; 217 | 218 | widgets_len = json_array_size(widgets); 219 | widgets_active = calloc(widgets_len, sizeof(struct widget)); 220 | 221 | LOG_DEBUG("starting %i widget threads", widgets_len); 222 | json_array_foreach(widgets, index, widget) { 223 | widgets_active[index] = spawn_widget(bar, 224 | context, 225 | json_object_get(widget, "config"), 226 | json_string_value(json_object_get(widget, "module"))); 227 | } 228 | 229 | /* lock mutex until the web page has been loaded completely */ 230 | pthread_mutex_lock(&web_view_ready_mutex); 231 | } 232 | -------------------------------------------------------------------------------- /src/widgets.h: -------------------------------------------------------------------------------- 1 | #ifndef WIDGETS_H 2 | #define WIDGETS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "util/config.h" 14 | #include "util/copy_prop.h" 15 | #include "util/gdk_helpers.h" 16 | #include "util/log.h" 17 | 18 | struct js_callback_arg { 19 | JSType type; 20 | union { 21 | JSObjectRef object; 22 | bool boolean; 23 | char *null; 24 | const char *string; 25 | int number; 26 | } value; 27 | }; 28 | 29 | struct js_callback_data { 30 | struct widget *widget; 31 | struct js_callback_arg *args; 32 | int args_len; 33 | }; 34 | 35 | struct widget { 36 | char *name; 37 | char *type; 38 | json_t *config; 39 | char *data; 40 | struct bar *bar; 41 | JSContextRef js_context; 42 | JSObjectRef js_object; 43 | JSStaticFunction *js_staticfuncs; 44 | pthread_t thread; 45 | pthread_mutex_t exit_mutex; 46 | pthread_cond_t exit_cond; 47 | }; 48 | 49 | typedef void (*widget_main_t)(void*); 50 | typedef char*(*widget_type_t)(); 51 | 52 | pthread_mutex_t web_view_ready_mutex; 53 | pthread_mutex_t update_mutex; 54 | pthread_cond_t update_cond; 55 | 56 | void join_widget_threads (); 57 | bool web_view_callback (struct js_callback_data *data); 58 | void wk_load_status_cb (GObject *object, GParamSpec *pspec, gpointer data); 59 | void wk_window_object_cleared_cb (WebKitWebView *web_view, GParamSpec *pspec, void *context, void *window_object, void *user_data); 60 | 61 | #define widget_init_config_string(WIDGET, KEY, TARGET) \ 62 | { json_t *CONF = get_config_option(WIDGET, KEY, true); \ 63 | if (CONF != NULL) { TARGET = json_string_value(CONF); } } 64 | #define widget_init_config_integer(WIDGET, KEY, TARGET) \ 65 | { json_t *CONF = get_config_option(WIDGET, KEY, true); \ 66 | if (CONF != NULL) { TARGET = json_integer_value(CONF); } } 67 | #define widget_init_config_real(WIDGET, KEY, TARGET) \ 68 | { json_t *CONF = get_config_option(WIDGET, KEY, true); \ 69 | if (CONF != NULL) { TARGET = json_real_value(CONF); } } 70 | #define widget_init_config_boolean(WIDGET, KEY, TARGET) \ 71 | { json_t *CONF = get_config_option(WIDGET, KEY, true); \ 72 | if (CONF != NULL) { TARGET = json_is_true(CONF); } } 73 | 74 | /* this macro waits until web view has loaded completely, the load-status 75 | callback unlocks the web_view_ready_mutex when the web view has loaded. this 76 | has to be done to ensure that JS callbacks are available. */ 77 | #define widget_data_callback(WIDGET, ...) \ 78 | { struct js_callback_arg args[] = { __VA_ARGS__ }; \ 79 | struct js_callback_data data = { .widget = WIDGET, .args = args, .args_len = LENGTH(args) }; \ 80 | pthread_mutex_lock(&web_view_ready_mutex); /* wait for web view */ \ 81 | pthread_mutex_unlock(&web_view_ready_mutex); \ 82 | pthread_mutex_lock(&update_mutex); /* lock while updating web view */ \ 83 | g_idle_add((GSourceFunc)web_view_callback, &data); \ 84 | pthread_cond_wait(&update_cond, &update_mutex); \ 85 | pthread_mutex_unlock(&update_mutex); } 86 | #define widget_data_arg_boolean(ARG) { .type = kJSTypeBoolean, .value.boolean = ARG } 87 | #define widget_data_arg_null() { .type = kJSTypeNull, .value.null = NULL } 88 | #define widget_data_arg_number(ARG) { .type = kJSTypeNumber, .value.number = ARG } 89 | #define widget_data_arg_object(ARG) { .type = kJSTypeObject, .value.object = ARG } 90 | #define widget_data_arg_string(ARG) { .type = kJSTypeString, .value.string = ARG } 91 | #define widget_data_arg_undefined() { .type = kJSTypeUndefined, .value.null = NULL } 92 | 93 | #define MAX_EVENTS 10 94 | #define widget_epoll_init(WIDGET) \ 95 | int efd = 0, nfds = 0; \ 96 | struct epoll_event event, events[MAX_EVENTS]; \ 97 | if ((efd = epoll_create1(0)) == -1) { LOG_ERR("failed to create epoll instance: %s", strerror(errno)); return 0; } \ 98 | event.data.fd = WIDGET->bar->efd; event.events = EPOLLIN | EPOLLET; \ 99 | if (epoll_ctl(efd, EPOLL_CTL_ADD, WIDGET->bar->efd, &event) == -1) { LOG_ERR("failed to add fd to epoll instance: %s", strerror(errno)); return 0; } 100 | #define widget_epoll_cleanup(WIDGET) \ 101 | if (epoll_ctl(efd, EPOLL_CTL_DEL, WIDGET->bar->efd, &event) == -1) { LOG_ERR("failed to remove fd from epoll instance: %s", strerror(errno)); return 0; } \ 102 | if (close(efd) != 0) { LOG_ERR("failed to close epoll file descriptor: %s", strerror(errno)); return 0; } 103 | #define widget_epoll_wait_goto(WIDGET, TIMEOUT_SECONDS, GOTO_LABEL) \ 104 | while ((nfds = epoll_wait(efd, events, MAX_EVENTS, TIMEOUT_SECONDS * 1000)) > 0) { \ 105 | for (unsigned short i = 0; i < nfds; i++) { \ 106 | if (events[i].data.fd == widget->bar->efd) { \ 107 | goto GOTO_LABEL; \ 108 | } } } 109 | #define widget_clean_exit(WIDGET) \ 110 | pthread_mutex_lock(&WIDGET->exit_mutex); \ 111 | pthread_cond_signal(&WIDGET->exit_cond); \ 112 | pthread_mutex_unlock(&WIDGET->exit_mutex); \ 113 | pthread_exit(0); 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /src/widgets/battery.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "battery.h" 3 | 4 | static int 5 | widget_update (struct widget *widget, DBusGProxy *properties_proxy, char *dbus_path) { 6 | gdouble percentage = 0; 7 | guint state = 0; 8 | gint64 time_to_empty = 0, time_to_full = 0; 9 | 10 | proxy_double_value(&percentage, properties_proxy, dbus_path, "Percentage"); 11 | proxy_uint_value(&state, properties_proxy, dbus_path, "State"); 12 | proxy_int64_value(&time_to_empty, properties_proxy, dbus_path, "TimeToEmpty"); 13 | proxy_int64_value(&time_to_full, properties_proxy, dbus_path, "TimeToFull"); 14 | 15 | widget_data_callback(widget, 16 | widget_data_arg_number(percentage), 17 | widget_data_arg_number(state), 18 | widget_data_arg_number(time_to_empty), 19 | widget_data_arg_number(time_to_full)); 20 | 21 | return 0; 22 | } 23 | 24 | void* 25 | widget_main (struct widget *widget) { 26 | struct widget_config config = widget_config_defaults; 27 | widget_init_config_string(widget->config, "dbus_path", config.dbus_path); 28 | widget_init_config_integer(widget->config, "refresh_interval", config.refresh_interval); 29 | widget_epoll_init(widget); 30 | 31 | DBusGConnection *conn = NULL; 32 | DBusGProxy *proxy = NULL; 33 | DBusGProxy *properties_proxy = NULL; 34 | GError *error = NULL; 35 | 36 | conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); 37 | 38 | if (conn == NULL) { 39 | LOG_ERR("dbus: failed to open connection to bus: %s\n", error->message); 40 | goto cleanup; 41 | } 42 | 43 | proxy = dbus_g_proxy_new_for_name(conn, "org.freedesktop.UPower", (char*)config.dbus_path, 44 | "org.freedesktop.UPower.Device.Properties"); 45 | if (proxy == NULL) { 46 | LOG_ERR("dbus: failed to create proxy object"); 47 | goto cleanup; 48 | } 49 | properties_proxy = dbus_g_proxy_new_from_proxy(proxy, "org.freedesktop.DBus.Properties", 50 | dbus_g_proxy_get_path(proxy)); 51 | if (properties_proxy == NULL) { 52 | LOG_ERR("dbus: failed to create proxy object"); 53 | goto cleanup; 54 | } 55 | 56 | unsigned int state; 57 | if (!proxy_uint_value(&state, properties_proxy, (char*)config.dbus_path, "State")) { 58 | LOG_ERR("dbus: invalid battery"); 59 | if (proxy != NULL) { 60 | g_object_unref(proxy); 61 | } 62 | if (properties_proxy != NULL) { 63 | g_object_unref(properties_proxy); 64 | } 65 | goto cleanup; 66 | } 67 | 68 | while (true) { 69 | widget_update(widget, properties_proxy, (char*)config.dbus_path); 70 | widget_epoll_wait_goto(widget, config.refresh_interval, cleanup); 71 | } 72 | 73 | cleanup: 74 | if (error != NULL) { 75 | g_error_free(error); 76 | } 77 | if (proxy != NULL) { 78 | g_object_unref(proxy); 79 | } 80 | if (properties_proxy != NULL) { 81 | g_object_unref(properties_proxy); 82 | } 83 | 84 | widget_epoll_cleanup(widget); 85 | widget_clean_exit(widget); 86 | } 87 | -------------------------------------------------------------------------------- /src/widgets/battery.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util/dbus_helpers.h" 5 | 6 | static struct widget_config { 7 | const char *dbus_path; 8 | int refresh_interval; 9 | } widget_config_defaults = { 10 | .dbus_path = "/org/freedesktop/UPower/devices/battery_BAT0", 11 | .refresh_interval = 20, 12 | }; 13 | -------------------------------------------------------------------------------- /src/widgets/datetime.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "datetime.h" 3 | 4 | static int 5 | widget_update (struct widget *widget, struct widget_config config) { 6 | time_t t; 7 | struct tm *tmp; 8 | char timestr[200]; 9 | char datestr[200]; 10 | 11 | t = time(NULL); 12 | tmp = localtime(&t); 13 | if (tmp == NULL) { 14 | perror("localtime"); 15 | 16 | return 0; 17 | } 18 | 19 | strftime(datestr, sizeof(datestr), config.date_format, tmp); 20 | strftime(timestr, sizeof(timestr), config.time_format, tmp); 21 | 22 | widget_data_callback(widget, 23 | widget_data_arg_string(datestr), 24 | widget_data_arg_string(timestr)); 25 | 26 | return 0; 27 | } 28 | 29 | void* 30 | widget_main (struct widget *widget) { 31 | struct widget_config config = widget_config_defaults; 32 | widget_init_config_string(widget->config, "date_format", config.date_format); 33 | widget_init_config_string(widget->config, "time_format", config.time_format); 34 | widget_init_config_integer(widget->config, "refresh_interval", config.refresh_interval); 35 | 36 | widget_epoll_init(widget); 37 | while (true) { 38 | widget_update(widget, config); 39 | widget_epoll_wait_goto(widget, config.refresh_interval, cleanup); 40 | } 41 | 42 | cleanup: 43 | 44 | widget_epoll_cleanup(widget); 45 | widget_clean_exit(widget); 46 | } 47 | -------------------------------------------------------------------------------- /src/widgets/datetime.h: -------------------------------------------------------------------------------- 1 | static struct widget_config { 2 | const char *date_format; 3 | const char *time_format; 4 | int refresh_interval; 5 | } widget_config_defaults = { 6 | .date_format = "%Y-%m-%d", 7 | .time_format = "%H:%M", 8 | .refresh_interval = 10, 9 | }; 10 | -------------------------------------------------------------------------------- /src/widgets/desktops.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "desktops.h" 3 | 4 | static int 5 | widget_update (struct widget *widget, xcb_ewmh_connection_t *ewmh, int screen_nbr, struct widget_config config) { 6 | unsigned short i; 7 | uint32_t desktop_curr, desktop_len, client_desktop; 8 | char desktop_name[COPY_PROP_BUFSIZ]; 9 | xcb_ewmh_get_utf8_strings_reply_t desktop_names; 10 | xcb_ewmh_get_windows_reply_t clients; 11 | xcb_icccm_wm_hints_t window_hints; 12 | struct desktop *desktops; 13 | 14 | /* get current desktop */ 15 | int desktop_curr_success = xcb_ewmh_get_current_desktop_reply(ewmh, xcb_ewmh_get_current_desktop_unchecked(ewmh, screen_nbr), &desktop_curr, NULL); 16 | if (!desktop_curr_success) { 17 | LOG_DEBUG("ewmh: could not get current desktop"); 18 | 19 | return 1; 20 | } 21 | 22 | /* get desktop count */ 23 | int desktop_len_success = xcb_ewmh_get_number_of_desktops_reply(ewmh, xcb_ewmh_get_number_of_desktops_unchecked(ewmh, screen_nbr), &desktop_len, NULL); 24 | if (!desktop_len_success) { 25 | LOG_DEBUG("ewmh: could not get desktop count"); 26 | 27 | return 2; 28 | } 29 | 30 | desktops = calloc(desktop_len, sizeof(struct desktop)); 31 | 32 | int desktop_names_success = xcb_ewmh_get_desktop_names_reply(ewmh, xcb_ewmh_get_desktop_names_unchecked(ewmh, screen_nbr), &desktop_names, NULL); 33 | if (!desktop_names_success) { 34 | LOG_DEBUG("ewmh: could not get desktop names"); 35 | } 36 | 37 | for (i = 0; i < desktop_len; i++) { 38 | desktops[i].is_selected = i == desktop_curr; 39 | desktops[i].is_urgent = false; 40 | desktops[i].clients_len = 0; 41 | 42 | if (desktop_names_success && desktop_names.strings) { 43 | copy_prop(desktop_name, desktop_names.strings, desktop_names.strings_len, i, desktop_len); 44 | } 45 | else { 46 | snprintf(desktop_name, COPY_PROP_BUFSIZ - 1, "%i", i + 1); 47 | } 48 | desktops[i].name = strndup(desktop_name, strlen(desktop_name)); 49 | } 50 | 51 | /* get clients */ 52 | int clients_success = xcb_ewmh_get_client_list_reply(ewmh, xcb_ewmh_get_client_list_unchecked(ewmh, screen_nbr), &clients, NULL); 53 | if (!clients_success) { 54 | LOG_DEBUG("ewmh: could not get client list"); 55 | } 56 | else { 57 | for (i = 0; i < clients.windows_len; i++) { 58 | if (!xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop_unchecked(ewmh, clients.windows[i]), &client_desktop, NULL)) { 59 | /* window isn't associated with a desktop */ 60 | continue; 61 | } 62 | desktops[client_desktop].clients_len++; 63 | 64 | /* check icccm urgency hint on client */ 65 | if (!xcb_icccm_get_wm_hints_reply(ewmh->connection, xcb_icccm_get_wm_hints_unchecked(ewmh->connection, clients.windows[i]), &window_hints, NULL)) { 66 | LOG_DEBUG("icccm: could not get window hints"); 67 | } 68 | if (window_hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY) { 69 | desktops[client_desktop].is_urgent = true; 70 | } 71 | } 72 | } 73 | 74 | json_t *json_data_object = json_object(); 75 | json_t *json_desktops_array = json_array(); 76 | json_object_set_new(json_data_object, "desktops", json_desktops_array); 77 | 78 | uint32_t desktop_idx = 0; 79 | for (i = 0; i < desktop_len; i++) { 80 | /* Hide desktop if not config.show_empty and empty/unselected */ 81 | if (!(config.show_empty) && !desktops[i].is_selected && (desktops[i].clients_len == 0)) { 82 | continue; 83 | } 84 | 85 | json_t *json_desktop = json_object(); 86 | json_object_set_new(json_desktop, "name", json_string(desktops[i].name)); 87 | json_object_set_new(json_desktop, "clients_len", json_integer(desktops[i].clients_len)); 88 | json_object_set_new(json_desktop, "is_urgent", json_boolean(desktops[i].is_urgent)); 89 | json_array_append_new(json_desktops_array, json_desktop); 90 | 91 | if (desktops[i].is_selected) { 92 | json_object_set_new(json_data_object, "current_desktop", json_integer(desktop_idx)); 93 | } 94 | desktop_idx++; 95 | } 96 | 97 | char *json_str = strdup(json_dumps(json_data_object, 0)); 98 | widget_data_callback(widget, widget_data_arg_string(json_str)); 99 | 100 | json_decref(json_data_object); 101 | free(json_str); 102 | 103 | /* cleanup */ 104 | if (desktop_names_success) { 105 | xcb_ewmh_get_utf8_strings_reply_wipe(&desktop_names); 106 | } 107 | if (clients_success) { 108 | xcb_ewmh_get_windows_reply_wipe(&clients); 109 | } 110 | for (i = 0; i < desktop_len; i++) { 111 | free(desktops[i].name); 112 | } 113 | free(desktops); 114 | 115 | return 0; 116 | } 117 | 118 | void* 119 | widget_main (struct widget *widget) { 120 | unsigned short i; 121 | int xcb_fd; 122 | int screen_nbr = 0; 123 | xcb_connection_t *conn = xcb_connect(NULL, NULL); 124 | xcb_ewmh_connection_t *ewmh = malloc(sizeof(xcb_ewmh_connection_t)); 125 | struct epoll_event xcb_event; 126 | 127 | struct widget_config config = widget_config_defaults; 128 | widget_init_config_boolean(widget->config, "show_empty", config.show_empty); 129 | 130 | widget_epoll_init(widget); 131 | 132 | if (xcb_connection_has_error(conn)) { 133 | LOG_ERR("could not connect to display %s", getenv("DISPLAY")); 134 | goto cleanup; 135 | } 136 | 137 | xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(conn, ewmh); 138 | xcb_ewmh_init_atoms_replies(ewmh, ewmh_cookie, NULL); 139 | 140 | uint32_t values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; 141 | xcb_generic_event_t *evt; 142 | xcb_generic_error_t *err = xcb_request_check(ewmh->connection, 143 | xcb_change_window_attributes_checked(ewmh->connection, 144 | ewmh->screens[screen_nbr]->root, 145 | XCB_CW_EVENT_MASK, 146 | values)); 147 | 148 | if (err != NULL) { 149 | LOG_ERR("could not request EWMH property change notifications"); 150 | goto cleanup; 151 | } 152 | 153 | xcb_fd = xcb_get_file_descriptor(ewmh->connection); 154 | xcb_event.data.fd = xcb_fd; 155 | xcb_event.events = EPOLLIN | EPOLLET; 156 | if (epoll_ctl(efd, EPOLL_CTL_ADD, xcb_fd, &xcb_event) == -1) { 157 | LOG_ERR("failed to add fd to epoll instance: %s", strerror(errno)); 158 | 159 | return 0; 160 | } 161 | 162 | widget_update(widget, ewmh, screen_nbr, config); 163 | while (true) { 164 | while ((nfds = epoll_wait(efd, events, MAX_EVENTS, -1)) > 0) { 165 | for (i = 0; i < nfds; i++) { 166 | if (events[i].data.fd == widget->bar->efd) { 167 | goto cleanup; 168 | } 169 | } 170 | 171 | while ((evt = xcb_poll_for_event(ewmh->connection)) != NULL) { 172 | xcb_property_notify_event_t *pne; 173 | switch (XCB_EVENT_RESPONSE_TYPE(evt)) { 174 | case XCB_PROPERTY_NOTIFY: 175 | pne = (xcb_property_notify_event_t*)evt; 176 | if (pne->atom == ewmh->_NET_DESKTOP_NAMES) { 177 | widget_update(widget, ewmh, screen_nbr, config); 178 | } 179 | else if (pne->atom == ewmh->_NET_NUMBER_OF_DESKTOPS) { 180 | widget_update(widget, ewmh, screen_nbr, config); 181 | } 182 | else if (pne->atom == ewmh->_NET_CURRENT_DESKTOP) { 183 | widget_update(widget, ewmh, screen_nbr, config); 184 | } 185 | default: 186 | break; 187 | } 188 | free(evt); 189 | } 190 | } 191 | } 192 | 193 | cleanup: 194 | if (ewmh != NULL) { 195 | xcb_ewmh_connection_wipe(ewmh); 196 | } 197 | if (conn != NULL) { 198 | xcb_disconnect(conn); 199 | } 200 | 201 | widget_epoll_cleanup(widget); 202 | widget_clean_exit(widget); 203 | } 204 | -------------------------------------------------------------------------------- /src/widgets/desktops.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct desktop { 7 | char *name; 8 | bool is_selected; 9 | bool is_urgent; 10 | int clients_len; 11 | }; 12 | 13 | static struct widget_config { 14 | bool show_empty; 15 | } widget_config_defaults = { 16 | .show_empty = true, 17 | }; 18 | -------------------------------------------------------------------------------- /src/widgets/desktops_i3.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "desktops_i3.h" 3 | 4 | char* 5 | widget_type () { 6 | return "desktops"; 7 | } 8 | 9 | gint 10 | workspace_comparator (gconstpointer pointer_a, gconstpointer pointer_b) { 11 | i3ipcWorkspaceReply *a = (i3ipcWorkspaceReply*)pointer_a; 12 | i3ipcWorkspaceReply *b = (i3ipcWorkspaceReply*)pointer_b; 13 | 14 | if (a->num < b->num) { 15 | return -1; 16 | } 17 | else if (a->num > b->num) { 18 | return 1; 19 | } 20 | else { 21 | return 0; 22 | } 23 | } 24 | 25 | json_t* 26 | format_workspaces (i3ipcConnection *conn) { 27 | json_t *json_data_object = json_object(); 28 | json_t *json_desktops_array = json_array(); 29 | json_object_set_new(json_data_object, "desktops", json_desktops_array); 30 | 31 | GSList *workspaces = i3ipc_connection_get_workspaces(conn, NULL); 32 | workspaces = g_slist_sort(workspaces, (GCompareFunc)workspace_comparator); 33 | for (unsigned char i = 0; i < g_slist_length(workspaces); i++) { 34 | GSList *workspace = g_slist_nth(workspaces, i); 35 | i3ipcWorkspaceReply *data = workspace->data; 36 | 37 | json_t *json_desktop = json_object(); 38 | json_object_set_new(json_desktop, "name", json_string(data->name)); 39 | json_object_set_new(json_desktop, "clients_len", json_integer(i)); 40 | json_object_set_new(json_desktop, "is_urgent", json_boolean(data->urgent)); 41 | json_array_append_new(json_desktops_array, json_desktop); 42 | 43 | if (data->focused) { 44 | json_object_set_new(json_data_object, "current_desktop", json_integer(i)); 45 | } 46 | } 47 | 48 | return json_data_object; 49 | } 50 | 51 | void 52 | workspace_callback (i3ipcConnection *conn, i3ipcWorkspaceEvent *event, gpointer user_data) { 53 | struct widget *widget = (struct widget*)user_data; 54 | json_t *json_data_object = format_workspaces(conn); 55 | char *json_str = strdup(json_dumps(json_data_object, 0)); 56 | struct js_callback_arg arg = widget_data_arg_string(json_str); 57 | struct js_callback_data data = { .widget = widget, .args = &arg, 1 }; 58 | web_view_callback(&data); 59 | free(json_str); 60 | } 61 | 62 | void* 63 | widget_main (struct widget *widget) { 64 | i3ipcConnection *conn = i3ipc_connection_new(NULL, NULL); 65 | 66 | json_t *json_data_object = format_workspaces(conn); 67 | char *json_str = strdup(json_dumps(json_data_object, 0)); 68 | widget_data_callback(widget, widget_data_arg_string(json_str)); 69 | free(json_str); 70 | 71 | void (*callback)(i3ipcConnection*, i3ipcWorkspaceEvent*, gpointer); 72 | callback = workspace_callback; 73 | 74 | GClosure *closure = g_cclosure_new(G_CALLBACK(callback), widget, NULL); 75 | i3ipc_connection_on(conn, (const gchar*)"workspace", closure, NULL); 76 | 77 | widget_epoll_init(widget); 78 | while (true) { 79 | widget_epoll_wait_goto(widget, 1000, cleanup); 80 | } 81 | 82 | cleanup: 83 | g_object_unref(conn); 84 | 85 | widget_epoll_cleanup(widget); 86 | widget_clean_exit(widget); 87 | } 88 | -------------------------------------------------------------------------------- /src/widgets/desktops_i3.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | -------------------------------------------------------------------------------- /src/widgets/email_imap.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "email_imap.h" 3 | #include "util/process.h" 4 | #include 5 | #include 6 | 7 | struct Buffer { 8 | char *stdout_buffer, *stderr_buffer; 9 | size_t stdout_used, stdout_len, stderr_used, stderr_len; 10 | }; 11 | 12 | static inline void 13 | free_buffer (struct Buffer *buffer) { 14 | if (buffer->stdout_buffer) { 15 | free(buffer->stdout_buffer); 16 | buffer->stdout_buffer = 0; 17 | buffer->stdout_used = buffer->stdout_len = 0; 18 | } 19 | if (buffer->stderr_buffer) { 20 | free(buffer->stderr_buffer); 21 | buffer->stderr_buffer = 0; 22 | buffer->stderr_used = buffer->stderr_len = 0; 23 | } 24 | } 25 | 26 | static inline void 27 | free_process (struct Process *proc) { 28 | for (int i = 0; proc->argv[i]; ++i) { 29 | free(proc->argv[i]); 30 | } 31 | free(proc->argv); 32 | } 33 | 34 | static inline void 35 | write_buffer (char **buf, size_t *used, size_t *buf_len, const char *data, size_t data_len) { 36 | if (!*buf) { 37 | *buf = calloc(1024, 1); 38 | *buf_len = 1024; 39 | } 40 | while (*used + data_len >= *buf_len) { 41 | size_t add = *buf_len * 1.5; 42 | if (add < data_len) { 43 | add = data_len; 44 | } 45 | *buf_len += add; 46 | *buf = realloc(*buf, *buf_len); 47 | } 48 | memcpy(*buf + *used, data, data_len); 49 | *used += data_len; 50 | (*buf)[*used] = 0; 51 | assert(*used <= *buf_len); 52 | } 53 | 54 | void 55 | write_stdout (struct Process *proc, const char *data, size_t len) { 56 | struct Buffer *buf = (struct Buffer*)proc->user_data; 57 | write_buffer(&buf->stdout_buffer, &buf->stdout_used, &buf->stdout_len, data, len); 58 | } 59 | 60 | void 61 | write_stderr (struct Process *proc, const char *data, size_t len) { 62 | struct Buffer *buf = (struct Buffer*)proc->user_data; 63 | write_buffer(&buf->stderr_buffer, &buf->stderr_used, &buf->stderr_len, data, len); 64 | } 65 | 66 | static int 67 | widget_update (struct widget *widget, struct widget_config config) { 68 | CURL *curl; 69 | CURLcode status; 70 | char *data; 71 | long code; 72 | 73 | /* connect and handle IMAP responses */ 74 | curl = curl_easy_init(); 75 | data = malloc(CURL_BUF_SIZE); 76 | if (!curl || !data) { 77 | return 0; 78 | } 79 | 80 | write_result_t write_result = { 81 | .data = data, 82 | .pos = 0 83 | }; 84 | 85 | curl_easy_setopt(curl, CURLOPT_USERNAME, config.username); 86 | curl_easy_setopt(curl, CURLOPT_PASSWORD, config.password); 87 | curl_easy_setopt(curl, CURLOPT_URL, config.address); 88 | curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "SEARCH UNSEEN"); 89 | 90 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, candybar_curl_write_response); 91 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); 92 | 93 | if (config.ssl_verify) { 94 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 95 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 96 | } 97 | 98 | status = curl_easy_perform(curl); 99 | if (status != CURLE_OK) { 100 | LOG_ERR("unable to request data from %s (this error may be temporary): %s", 101 | config.address, 102 | curl_easy_strerror(status)); 103 | 104 | return 0; 105 | } 106 | 107 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); 108 | 109 | if (code != 0) { 110 | LOG_ERR("server responded with code %ld", code); 111 | 112 | return 0; 113 | } 114 | 115 | curl_easy_cleanup(curl); 116 | curl_global_cleanup(); 117 | 118 | data[write_result.pos] = '\0'; 119 | 120 | /* count unread message IDs */ 121 | 122 | /* the server responds with a string like "* SEARCH 1 2 3 4" where the 123 | numbers are message IDs */ 124 | char *str, *saveptr, *delim = " *"; 125 | int unread = -1; 126 | 127 | str = strtok_r(data, delim, &saveptr); 128 | while (str != NULL) { 129 | str = strtok_r(NULL, delim, &saveptr); 130 | unread++; 131 | } 132 | 133 | free(data); 134 | 135 | widget_data_callback(widget, 136 | widget_data_arg_number(unread), 137 | widget_data_arg_string(config.username)); 138 | 139 | return 0; 140 | } 141 | 142 | static const char* 143 | next (const char *ch, int space) { 144 | while (*ch && !isspace(*ch) == !space) { 145 | ++ch; 146 | } 147 | 148 | return ch; 149 | } 150 | 151 | static int 152 | parse_cmdline_arguments (const char *password_command, struct Process *proc) { 153 | const char *start = next(password_command, 1); 154 | if (!*start) { 155 | return -1; 156 | } 157 | const char *end = next(start, 0); 158 | if (!*end) { 159 | proc->path = strdup(start); 160 | } 161 | else { 162 | proc->path = strndup(start, end - start); 163 | } 164 | proc->argv = calloc(1024, sizeof(char*)); 165 | proc->argv[0] = proc->path; 166 | int argv_idx = 1; 167 | 168 | while ((start = next(end, 1))) { 169 | end = next(start, 0); 170 | if (!*end) { 171 | proc->argv[argv_idx++] = strdup(start); 172 | break; 173 | } 174 | else { 175 | proc->argv[argv_idx++] = strndup(start, end - start); 176 | } 177 | } 178 | 179 | return 1; 180 | } 181 | 182 | void* 183 | widget_main (struct widget *widget) { 184 | struct widget_config config = widget_config_defaults; 185 | widget_init_config_string(widget->config, "address", config.address); 186 | widget_init_config_string(widget->config, "username", config.username); 187 | widget_init_config_string(widget->config, "password", config.password); 188 | widget_init_config_string(widget->config, "password_command", config.password_command); 189 | widget_init_config_boolean(widget->config, "ssl_verify", config.ssl_verify); 190 | widget_init_config_integer(widget->config, "refresh_interval", config.refresh_interval); 191 | 192 | if (!config.username) { 193 | LOG_INFO("email_imap: username not set, disabling widget"); 194 | 195 | return 0; 196 | } 197 | 198 | struct Buffer buffer; 199 | memset(&buffer, 0, sizeof(buffer)); 200 | if (strlen(config.password_command)) { 201 | struct Process proc; 202 | memset(&proc, 0, sizeof(proc)); 203 | proc.stderr_cb = write_stderr; 204 | proc.stdout_cb = write_stdout; 205 | proc.user_data = &buffer; 206 | if (parse_cmdline_arguments(config.password_command, &proc) == -1) { 207 | LOG_ERR("email_imap: cannot parse password_command: \"%s\"", config.password_command); 208 | 209 | return 0; 210 | } 211 | 212 | const int ret = process(&proc); 213 | free_process(&proc); 214 | if (ret != 0) { 215 | LOG_ERR("email_imap: process error: %s => %d: %s/%s\n", proc.path, ret, proc.error, buffer.stderr_buffer); 216 | 217 | return 0; 218 | } 219 | config.password = buffer.stdout_buffer; 220 | } 221 | 222 | widget_epoll_init(widget); 223 | while (true) { 224 | widget_update(widget, config); 225 | widget_epoll_wait_goto(widget, config.refresh_interval, cleanup); 226 | } 227 | 228 | cleanup: 229 | 230 | widget_epoll_cleanup(widget); 231 | widget_clean_exit(widget); 232 | free_buffer(&buffer); 233 | } 234 | -------------------------------------------------------------------------------- /src/widgets/email_imap.h: -------------------------------------------------------------------------------- 1 | #include "util/curl.h" 2 | 3 | static struct widget_config { 4 | const char *address; 5 | const char *username; 6 | const char *password; 7 | const char *password_command; 8 | bool ssl_verify; 9 | int refresh_interval; 10 | } widget_config_defaults = { 11 | .address = "imaps://imap.gmail.com:993/INBOX", 12 | .username = "", 13 | .password = "", 14 | .password_command = "", 15 | .ssl_verify = true, 16 | .refresh_interval = 60, 17 | }; 18 | -------------------------------------------------------------------------------- /src/widgets/external_ip.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "external_ip.h" 3 | 4 | void* 5 | widget_main (struct widget *widget) { 6 | char *external_ip; 7 | struct widget_config config = widget_config_defaults; 8 | widget_init_config_string(widget->config, "address", config.address); 9 | widget_init_config_integer(widget->config, "refresh_interval", config.refresh_interval); 10 | 11 | widget_epoll_init(widget); 12 | while (true) { 13 | external_ip = candybar_curl_request(config.address); 14 | widget_data_callback(widget, widget_data_arg_string(external_ip)); 15 | free(external_ip); 16 | 17 | widget_epoll_wait_goto(widget, config.refresh_interval, cleanup); 18 | } 19 | 20 | cleanup: 21 | 22 | widget_epoll_cleanup(widget); 23 | widget_clean_exit(widget); 24 | } 25 | -------------------------------------------------------------------------------- /src/widgets/external_ip.h: -------------------------------------------------------------------------------- 1 | #include "util/curl.h" 2 | 3 | static struct widget_config { 4 | const char *address; 5 | int refresh_interval; 6 | } widget_config_defaults = { 7 | .address = "http://ipv4.icanhazip.com/", 8 | .refresh_interval = 3600, 9 | }; 10 | -------------------------------------------------------------------------------- /src/widgets/magick_background.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "magick_background.h" 3 | 4 | static 5 | xcb_screen_t* 6 | screen_of_display (xcb_connection_t *c, int screen) { 7 | xcb_screen_iterator_t iter; 8 | 9 | iter = xcb_setup_roots_iterator(xcb_get_setup(c)); 10 | for (; iter.rem; --screen, xcb_screen_next(&iter)) { 11 | if (screen == 0) { 12 | return iter.data; 13 | } 14 | } 15 | 16 | return NULL; 17 | } 18 | 19 | void* 20 | widget_main (struct widget *widget) { 21 | struct widget_config config = widget_config_defaults; 22 | widget_init_config_string(widget->config, "image", config.image); 23 | widget_init_config_string(widget->config, "css_gradient_overlay", config.css_gradient_overlay); 24 | widget_init_config_integer(widget->config, "blur_radius", config.blur_radius); 25 | widget_init_config_integer(widget->config, "brightness", config.brightness); 26 | widget_init_config_integer(widget->config, "saturation", config.saturation); 27 | 28 | InitializeMagick(NULL); 29 | 30 | xcb_connection_t *conn = NULL; 31 | xcb_get_property_reply_t *pixmap_r = NULL; 32 | xcb_intern_atom_reply_t *atom_r = NULL; 33 | xcb_generic_error_t *err = NULL; 34 | xcb_get_image_reply_t *im_r = NULL; 35 | 36 | size_t img_len; 37 | void *img_data = NULL; 38 | char *img_base64 = NULL; 39 | Image *img = NULL; 40 | ImageInfo *img_info = CloneImageInfo(0); 41 | ExceptionInfo exception; 42 | GetExceptionInfo(&exception); 43 | 44 | if (!strlen(config.image)) { 45 | int screen_nbr = 0; 46 | conn = xcb_connect(NULL, NULL); 47 | if (xcb_connection_has_error(conn)) { 48 | LOG_ERR("X connection invalid"); 49 | goto cleanup; 50 | } 51 | xcb_intern_atom_cookie_t atom_c; 52 | xcb_get_property_cookie_t pixmap_c; 53 | xcb_get_image_cookie_t im_c; 54 | 55 | xcb_screen_t *screen = screen_of_display(conn, screen_nbr); 56 | xcb_drawable_t root_pixmap = XCB_NONE; 57 | const char *atom = "_XROOTPMAP_ID"; 58 | atom_c = xcb_intern_atom_unchecked(conn, false, strlen(atom), atom); 59 | atom_r = xcb_intern_atom_reply(conn, atom_c, NULL); 60 | if (!atom_r) { 61 | LOG_ERR("could not get %s atom", atom); 62 | goto cleanup; 63 | } 64 | 65 | pixmap_c = xcb_get_property_unchecked(conn, false, screen->root, atom_r->atom, XCB_ATOM_PIXMAP, 0, 1); 66 | if ((pixmap_r = xcb_get_property_reply(conn, pixmap_c, NULL))) { 67 | if (!pixmap_r->value_len) { 68 | LOG_ERR("could not get background pixmap"); 69 | goto cleanup; 70 | } 71 | root_pixmap = *(xcb_drawable_t*)xcb_get_property_value(pixmap_r); 72 | } 73 | 74 | im_c = xcb_get_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, root_pixmap, 75 | widget->bar->pos_x, 76 | widget->bar->pos_y, 77 | widget->bar->width, 78 | widget->bar->height, 79 | 0xffffffff); 80 | im_r = xcb_get_image_reply(conn, im_c, &err); 81 | if (err != NULL) { 82 | LOG_ERR("could not get background image"); 83 | goto cleanup; 84 | } 85 | 86 | uint8_t *data = xcb_get_image_data(im_r); 87 | 88 | img = ConstituteImage(widget->bar->width, widget->bar->height, "BGRA", CharPixel, data, &exception); 89 | strncpy(img->magick, "png", MaxTextExtent - 1); 90 | if (exception.severity != UndefinedException) { 91 | LOG_ERR("could not read background from root window: %s", exception.reason); 92 | goto cleanup; 93 | } 94 | } 95 | else { 96 | strncpy(img_info->filename, config.image, MaxTextExtent - 1); 97 | RectangleInfo geom = { widget->bar->width, widget->bar->height, 0, 0 }; 98 | img = ReadImage(img_info, &exception); 99 | strncpy(img->magick, "png", MaxTextExtent - 1); 100 | if (exception.severity != UndefinedException) { 101 | LOG_ERR("could not read image '%s': %s", config.image, exception.reason); 102 | goto cleanup; 103 | } 104 | img = CropImage(img, &geom, &exception); 105 | } 106 | 107 | if (config.blur_radius != 0) { 108 | img = GaussianBlurImage(img, config.blur_radius, config.blur_radius, &exception); 109 | } 110 | if ((config.brightness != 100) || (config.saturation != 100)) { 111 | char modulate_str[20]; 112 | snprintf(modulate_str, 19, "%i,%i,%i", config.brightness, 0, config.saturation); 113 | ModulateImage(img, (const char*)&modulate_str); 114 | } 115 | 116 | img_data = ImageToBlob(img_info, img, &img_len, &exception); 117 | if (exception.severity != UndefinedException) { 118 | LOG_ERR("could not write image blob: %s", exception.reason); 119 | goto cleanup; 120 | } 121 | 122 | img_base64 = g_base64_encode(img_data, img_len); 123 | 124 | widget_data_callback(widget, 125 | widget_data_arg_string(img_base64), 126 | widget_data_arg_string(config.css_gradient_overlay)); 127 | 128 | cleanup: 129 | g_free(img_base64); 130 | DestroyImageInfo(img_info); 131 | DestroyMagick(); 132 | if (conn != NULL) { 133 | xcb_disconnect(conn); 134 | } 135 | if (err != NULL) { 136 | free(err); 137 | } 138 | if (atom_r != NULL) { 139 | free(atom_r); 140 | } 141 | if (pixmap_r != NULL) { 142 | free(pixmap_r); 143 | } 144 | if (im_r != NULL) { 145 | free(im_r); 146 | } 147 | if (img_data != NULL) { 148 | free(img_data); 149 | } 150 | if (img != NULL) { 151 | free(img); 152 | } 153 | 154 | widget_clean_exit(widget); 155 | } 156 | -------------------------------------------------------------------------------- /src/widgets/magick_background.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static struct widget_config { 6 | const char *image; 7 | const char *css_gradient_overlay; 8 | int blur_radius; 9 | int brightness; 10 | int saturation; 11 | } widget_config_defaults = { 12 | .image = "", 13 | .css_gradient_overlay = "top, transparent, rgba(0, 0, 0, .5)", 14 | .blur_radius = 0, 15 | .brightness = 100, 16 | .saturation = 100, 17 | }; 18 | -------------------------------------------------------------------------------- /src/widgets/notifications.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "notifications.h" 3 | 4 | static char *server_info[] = { "candybar", "Lokaltog", "0.1", "1.2", NULL }; 5 | static char *server_capabilities[] = { "body", NULL }; 6 | 7 | static void 8 | dbus_array_reply (DBusConnection *connection, DBusMessage *msg, char *array[]) { 9 | DBusMessage *reply = dbus_message_new_method_return(msg); 10 | DBusMessageIter args; 11 | 12 | bool success = true; 13 | dbus_message_iter_init_append(reply, &args); 14 | int i; 15 | for (i = 0; array[i] != 0; i++) { 16 | if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &array[i])) { 17 | success = false; 18 | } 19 | } 20 | if (success && dbus_connection_send(connection, reply, NULL)) { 21 | dbus_message_unref(reply); 22 | } 23 | } 24 | 25 | static int 26 | widget_update (struct widget *widget, DBusConnection *connection, DBusMessage *msg) { 27 | unsigned short i; 28 | DBusMessage *reply; 29 | DBusMessageIter args; 30 | const char *appname; 31 | const char *summary; 32 | const char *body; 33 | dbus_uint32_t nid = 0; 34 | dbus_int32_t expires = -1; 35 | void *to_fill = NULL; 36 | 37 | dbus_message_iter_init(msg, &args); 38 | for (i = 0; i < 8; i++) { 39 | switch (i) { 40 | case 0: 41 | to_fill = &appname; 42 | break; 43 | case 1: 44 | to_fill = &nid; 45 | break; 46 | case 3: 47 | to_fill = &summary; 48 | break; 49 | case 4: 50 | to_fill = &body; 51 | break; 52 | case 7: 53 | to_fill = &expires; 54 | break; 55 | default: 56 | to_fill = NULL; 57 | break; 58 | } 59 | if (to_fill) { 60 | dbus_message_iter_get_basic(&args, to_fill); 61 | } 62 | dbus_message_iter_next(&args); 63 | } 64 | 65 | /* send reply */ 66 | reply = dbus_message_new_method_return(msg); 67 | dbus_message_iter_init_append(reply, &args); 68 | if (dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &nid)) { 69 | dbus_connection_send(connection, reply, NULL); 70 | } 71 | dbus_message_unref(reply); 72 | 73 | widget_data_callback(widget, 74 | widget_data_arg_string(appname), 75 | widget_data_arg_string(summary), 76 | widget_data_arg_string(body), 77 | widget_data_arg_number(expires)); 78 | 79 | return 0; 80 | } 81 | 82 | static void 83 | widget_cleanup (void *arg) { 84 | LOG_DEBUG("cleanup"); 85 | 86 | void **cleanup_data = arg; 87 | 88 | if (cleanup_data[0] != NULL) { 89 | dbus_connection_unref(cleanup_data[0]); 90 | } 91 | } 92 | 93 | void* 94 | widget_main (struct widget *widget) { 95 | DBusConnection *connection; 96 | DBusError dbus_error; 97 | DBusError *err = &dbus_error; 98 | DBusMessage *msg; 99 | int server_result; 100 | 101 | dbus_error_init(err); 102 | connection = dbus_bus_get(DBUS_BUS_SESSION, err); 103 | if (dbus_error_is_set(err)) { 104 | LOG_ERR("dbus connection error: %s", err->message); 105 | goto cleanup; 106 | } 107 | if (!connection) { 108 | LOG_ERR("dbus: no connection"); 109 | goto cleanup; 110 | } 111 | 112 | server_result = dbus_bus_request_name(connection, "org.freedesktop.Notifications", DBUS_NAME_FLAG_REPLACE_EXISTING, err); 113 | if (dbus_error_is_set(err)) { 114 | LOG_ERR("dbus error: %s", err->message); 115 | goto cleanup; 116 | } 117 | if (server_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { 118 | LOG_WARN("dbus: a notification daemon is already running"); 119 | goto cleanup; 120 | } 121 | if (err != NULL) { 122 | dbus_error_free(err); 123 | } 124 | 125 | void *cleanup_data[] = { connection }; 126 | pthread_cleanup_push(widget_cleanup, &cleanup_data); 127 | for (;;) { 128 | dbus_connection_read_write(connection, -1); 129 | 130 | while ((msg = dbus_connection_pop_message(connection)) != NULL) { 131 | if (dbus_message_is_method_call(msg, "org.freedesktop.Notifications", "Notify")) { 132 | if (widget_update(widget, connection, msg) != 0) { 133 | LOG_ERR("dbus: error while handling notification"); 134 | break; 135 | } 136 | } 137 | else if (dbus_message_is_method_call(msg, "org.freedesktop.Notifications", "GetServerInformation")) { 138 | dbus_array_reply(connection, msg, server_info); 139 | } 140 | else if (dbus_message_is_method_call(msg, "org.freedesktop.Notifications", "GetCapabilities")) { 141 | dbus_array_reply(connection, msg, server_capabilities); 142 | } 143 | 144 | dbus_message_unref(msg); 145 | dbus_connection_flush(connection); 146 | } 147 | } 148 | pthread_cleanup_pop(1); 149 | 150 | cleanup: 151 | if (err != NULL) { 152 | dbus_error_free(err); 153 | } 154 | if (connection != NULL) { 155 | dbus_connection_unref(connection); 156 | } 157 | 158 | widget_clean_exit(widget); 159 | } 160 | -------------------------------------------------------------------------------- /src/widgets/notifications.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /src/widgets/now_playing_mpd.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "now_playing_mpd.h" 3 | 4 | static int 5 | widget_update (struct widget *widget, struct mpd_connection *connection) { 6 | struct mpd_song *song; 7 | struct mpd_status *status; 8 | enum mpd_state state; 9 | 10 | /* get mpd status */ 11 | mpd_send_status(connection); 12 | status = mpd_recv_status(connection); 13 | if (status == NULL) { 14 | LOG_ERR("status error: %s", mpd_connection_get_error_message(connection)); 15 | 16 | return -1; 17 | } 18 | state = mpd_status_get_state(status); 19 | if (mpd_connection_get_error(connection) != MPD_ERROR_SUCCESS) { 20 | LOG_ERR("state error: %s", mpd_connection_get_error_message(connection)); 21 | 22 | return -1; 23 | } 24 | 25 | /* only update if playing/paused */ 26 | if (!((state == MPD_STATE_STOP) || (state == MPD_STATE_UNKNOWN))) { 27 | mpd_send_current_song(connection); 28 | 29 | while ((song = mpd_recv_song(connection)) != NULL) { 30 | widget_data_callback(widget, 31 | widget_data_arg_string(mpd_song_get_tag(song, MPD_TAG_TITLE, 0)), 32 | widget_data_arg_string(mpd_song_get_tag(song, MPD_TAG_ARTIST, 0)), 33 | widget_data_arg_string(mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)), 34 | widget_data_arg_number(mpd_status_get_total_time(status)), 35 | widget_data_arg_number(mpd_status_get_elapsed_time(status)), 36 | widget_data_arg_boolean(state == MPD_STATE_PLAY)); 37 | 38 | mpd_song_free(song); 39 | 40 | if (mpd_connection_get_error(connection) != MPD_ERROR_SUCCESS) { 41 | LOG_ERR("song error: %s", mpd_connection_get_error_message(connection)); 42 | 43 | return -1; 44 | } 45 | } 46 | } 47 | else { 48 | widget_data_callback(widget, widget_data_arg_null()); 49 | } 50 | 51 | mpd_status_free(status); 52 | mpd_send_idle_mask(connection, MPD_IDLE_PLAYER); 53 | 54 | return 0; 55 | } 56 | 57 | void* 58 | widget_main (struct widget *widget) { 59 | struct widget_config config = widget_config_defaults; 60 | widget_init_config_string(widget->config, "host", config.host); 61 | widget_init_config_integer(widget->config, "port", config.port); 62 | widget_init_config_integer(widget->config, "timeout", config.timeout); 63 | 64 | unsigned short i; 65 | int mpd_fd; 66 | struct mpd_connection *conn = mpd_connection_new(config.host, config.port, config.timeout); 67 | struct epoll_event mpd_event; 68 | 69 | if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { 70 | LOG_ERR("failed to connect to mpd server at %s:%i: %s", 71 | config.host, config.port, 72 | mpd_connection_get_error_message(conn)); 73 | mpd_connection_free(conn); 74 | 75 | return 0; 76 | } 77 | 78 | widget_epoll_init(widget); 79 | mpd_fd = mpd_connection_get_fd(conn); 80 | mpd_event.data.fd = mpd_fd; 81 | mpd_event.events = EPOLLIN | EPOLLET; 82 | if (epoll_ctl(efd, EPOLL_CTL_ADD, mpd_fd, &mpd_event) == -1) { 83 | LOG_ERR("failed to add fd to epoll instance: %s", strerror(errno)); 84 | 85 | return 0; 86 | } 87 | 88 | widget_update(widget, conn); 89 | while (true) { 90 | while ((nfds = epoll_wait(efd, events, MAX_EVENTS, -1)) > 0) { 91 | for (i = 0; i < nfds; i++) { 92 | if (events[i].data.fd == widget->bar->efd) { 93 | goto cleanup; 94 | } 95 | else if (events[i].data.fd == mpd_fd) { 96 | /* empty event buffer */ 97 | mpd_recv_idle(conn, true); 98 | if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { 99 | LOG_ERR("recv error: %s", mpd_connection_get_error_message(conn)); 100 | break; 101 | } 102 | widget_update(widget, conn); 103 | } 104 | } 105 | } 106 | } 107 | 108 | cleanup: 109 | mpd_connection_free(conn); 110 | 111 | widget_epoll_cleanup(widget); 112 | widget_clean_exit(widget); 113 | } 114 | -------------------------------------------------------------------------------- /src/widgets/now_playing_mpd.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static struct widget_config { 4 | const char *host; 5 | int port; 6 | int timeout; 7 | } widget_config_defaults = { 8 | .host = "localhost", 9 | .port = 6600, 10 | .timeout = 5000, 11 | }; 12 | -------------------------------------------------------------------------------- /src/widgets/now_playing_mpris.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "now_playing_mpris.h" 3 | 4 | static JSValueRef 5 | widget_js_func_toggle (JSContextRef ctx, JSObjectRef func, JSObjectRef this, size_t argc, const JSValueRef argv[], JSValueRef *exc) { 6 | PlayerctlPlayer *player = playerctl_player_new(NULL, NULL); 7 | playerctl_player_play_pause(player, NULL); 8 | 9 | g_object_unref(player); 10 | 11 | return JSValueMakeUndefined(ctx); 12 | } 13 | 14 | static JSValueRef 15 | widget_js_func_next (JSContextRef ctx, JSObjectRef func, JSObjectRef this, size_t argc, const JSValueRef argv[], JSValueRef *exc) { 16 | PlayerctlPlayer *player = playerctl_player_new(NULL, NULL); 17 | playerctl_player_next(player, NULL); 18 | 19 | g_object_unref(player); 20 | 21 | return JSValueMakeUndefined(ctx); 22 | } 23 | 24 | static JSValueRef 25 | widget_js_func_previous (JSContextRef ctx, JSObjectRef func, JSObjectRef this, size_t argc, const JSValueRef argv[], JSValueRef *exc) { 26 | PlayerctlPlayer *player = playerctl_player_new(NULL, NULL); 27 | playerctl_player_previous(player, NULL); 28 | 29 | g_object_unref(player); 30 | 31 | return JSValueMakeUndefined(ctx); 32 | } 33 | 34 | const JSStaticFunction widget_js_staticfuncs[] = { 35 | { "toggle", widget_js_func_toggle, kJSPropertyAttributeReadOnly }, 36 | { "next", widget_js_func_next, kJSPropertyAttributeReadOnly }, 37 | { "previous", widget_js_func_previous, kJSPropertyAttributeReadOnly }, 38 | { NULL, NULL, 0 }, 39 | }; 40 | 41 | static int 42 | update_widget (struct widget *widget, PlayerctlPlayer *player) { 43 | char *artist = playerctl_player_get_artist(player, NULL); 44 | char *title = playerctl_player_get_title(player, NULL); 45 | 46 | struct js_callback_arg args[2] = { 47 | widget_data_arg_string(artist), 48 | widget_data_arg_string(title), 49 | }; 50 | 51 | struct js_callback_data data = { 52 | .widget = widget, 53 | .args = args, 54 | 2, 55 | }; 56 | 57 | web_view_callback(&data); 58 | 59 | g_free(artist); 60 | g_free(title); 61 | 62 | return 0; 63 | } 64 | 65 | static void 66 | on_metadata (PlayerctlPlayer *player, GVariant *e, gpointer user_data) { 67 | struct widget *widget = user_data; 68 | update_widget(widget, player); 69 | } 70 | 71 | void* 72 | widget_main (struct widget *widget) { 73 | struct widget_config config = widget_config_defaults; 74 | widget_init_config_string(widget->config, "player_name", config.player_name); 75 | 76 | gchar *player_name = (gchar*)config.player_name; 77 | PlayerctlPlayer *player = NULL; 78 | 79 | widget_epoll_init(widget); 80 | while (true) { 81 | player = playerctl_player_new(player_name, NULL); 82 | 83 | if (player) { 84 | g_signal_connect(player, "metadata", G_CALLBACK(on_metadata), widget); 85 | 86 | widget_epoll_wait_goto(widget, -1, cleanup); 87 | } 88 | else { 89 | /* keep trying until we find a player */ 90 | widget_epoll_wait_goto(widget, 1, cleanup); 91 | } 92 | } 93 | 94 | cleanup: 95 | g_object_unref(player); 96 | 97 | widget_epoll_cleanup(widget); 98 | widget_clean_exit(widget); 99 | } 100 | -------------------------------------------------------------------------------- /src/widgets/now_playing_mpris.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static struct widget_config { 4 | const char *player_name; 5 | } widget_config_defaults = { 6 | .player_name = NULL 7 | }; 8 | -------------------------------------------------------------------------------- /src/widgets/volume.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "volume.h" 3 | 4 | static int 5 | widget_update (struct widget *widget, snd_mixer_elem_t *elem) { 6 | long volume_min, volume_max, volume; 7 | int active; 8 | 9 | snd_mixer_selem_get_playback_volume_range(elem, &volume_min, &volume_max); 10 | snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &volume); 11 | snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &active); 12 | 13 | widget_data_callback(widget, 14 | widget_data_arg_number(100 * (volume - volume_min) / (volume_max - volume_min)), 15 | widget_data_arg_boolean(active)); 16 | 17 | return 0; 18 | } 19 | 20 | void* 21 | widget_main (struct widget *widget) { 22 | struct widget_config config = widget_config_defaults; 23 | widget_init_config_string(widget->config, "card", config.card); 24 | widget_init_config_string(widget->config, "selem", config.selem); 25 | widget_epoll_init(widget); 26 | 27 | /* open mixer */ 28 | int err = 0; 29 | int mixer_fd; 30 | snd_mixer_selem_id_t *sid = NULL; 31 | snd_mixer_t *mixer = NULL; 32 | struct epoll_event mixer_event; 33 | struct pollfd *pollfds = NULL; 34 | unsigned short i; 35 | 36 | if ((err = snd_mixer_open(&mixer, 0)) < 0) { 37 | LOG_ERR("could not open mixer (%i)", err); 38 | goto cleanup; 39 | } 40 | if ((err = snd_mixer_attach(mixer, config.card)) < 0) { 41 | LOG_ERR("could not attach card '%s' to mixer (%i)", config.card, err); 42 | goto cleanup; 43 | } 44 | if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) { 45 | LOG_ERR("could not register mixer simple element class (%i)", err); 46 | goto cleanup; 47 | } 48 | if ((err = snd_mixer_load(mixer)) < 0) { 49 | LOG_ERR("could not load mixer (%i)", err); 50 | goto cleanup; 51 | } 52 | 53 | snd_mixer_selem_id_alloca(&sid); 54 | snd_mixer_selem_id_set_index(sid, 0); 55 | snd_mixer_selem_id_set_name(sid, config.selem); 56 | snd_mixer_elem_t *elem = snd_mixer_find_selem(mixer, sid); 57 | 58 | if (!elem) { 59 | LOG_ERR("could not find selem '%s'", config.selem); 60 | goto cleanup; 61 | } 62 | 63 | int pollfds_len = snd_mixer_poll_descriptors_count(mixer); 64 | pollfds = calloc(pollfds_len, sizeof(*pollfds)); 65 | err = snd_mixer_poll_descriptors(mixer, &pollfds[0], pollfds_len); 66 | if (err < 0) { 67 | LOG_ERR("alsa: can't get poll descriptors: %i", err); 68 | } 69 | 70 | for (i = 0; i < pollfds_len; i++) { 71 | mixer_fd = pollfds[i].fd; 72 | mixer_event.data.fd = mixer_fd; 73 | mixer_event.events = EPOLLIN | EPOLLET; 74 | if (epoll_ctl(efd, EPOLL_CTL_ADD, mixer_fd, &mixer_event) == -1) { 75 | LOG_ERR("failed to add fd to epoll instance: %s", strerror(errno)); 76 | 77 | return 0; 78 | } 79 | } 80 | 81 | widget_update(widget, elem); 82 | while (true) { 83 | while ((nfds = epoll_wait(efd, events, MAX_EVENTS, -1)) > 0) { 84 | for (i = 0; i < nfds; i++) { 85 | if (events[i].data.fd == widget->bar->efd) { 86 | goto cleanup; 87 | } 88 | } 89 | snd_mixer_handle_events(mixer); 90 | widget_update(widget, elem); 91 | } 92 | } 93 | 94 | cleanup: 95 | if (pollfds != NULL) { 96 | free(pollfds); 97 | } 98 | if (mixer != NULL) { 99 | snd_mixer_close(mixer); 100 | } 101 | 102 | widget_epoll_cleanup(widget); 103 | widget_clean_exit(widget); 104 | } 105 | -------------------------------------------------------------------------------- /src/widgets/volume.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static struct widget_config { 4 | const char *card; 5 | const char *selem; 6 | } widget_config_defaults = { 7 | .card = "default", 8 | .selem = "Master", 9 | }; 10 | -------------------------------------------------------------------------------- /src/widgets/weather.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "weather.h" 3 | 4 | static int 5 | get_geoip_location (struct location *location) { 6 | json_t *location_data, *geoip_city, *geoip_country_code; 7 | json_error_t error; 8 | 9 | char *geoip_raw_json = candybar_curl_request("http://freegeoip.net/json/"); 10 | location_data = json_loads(geoip_raw_json, 0, &error); 11 | 12 | if (!location_data) { 13 | LOG_WARN("error while fetching GeoIP data"); 14 | goto error; 15 | } 16 | 17 | free(geoip_raw_json); 18 | geoip_raw_json = NULL; 19 | 20 | geoip_city = json_object_get(location_data, "city"); 21 | if (!json_is_string(geoip_city)) { 22 | LOG_ERR("received GeoIP city is not a string"); 23 | goto error; 24 | } 25 | geoip_country_code = json_object_get(location_data, "country_code"); 26 | if (!json_is_string(geoip_country_code)) { 27 | LOG_ERR("received GeoIP country code is not a string"); 28 | goto error; 29 | } 30 | 31 | location->city = strdup(json_string_value(geoip_city)); 32 | location->country_code = strdup(json_string_value(geoip_country_code)); 33 | 34 | if (location_data != NULL) { 35 | json_decref(location_data); 36 | } 37 | 38 | return 0; 39 | 40 | error: 41 | free(geoip_raw_json); 42 | geoip_raw_json = NULL; 43 | 44 | json_decref(location_data); 45 | 46 | return 1; 47 | } 48 | 49 | static struct weather* 50 | get_weather_information (struct location *location) { 51 | int query_str_len, request_uri_len; 52 | char *query_str, *query_str_escaped, *request_uri; 53 | char *query_str_template = "select item.condition.code, item.condition.temp from weather.forecast " 54 | "where u = 'c' and woeid in (select woeid from geo.places where text = '%s %s' limit 1) limit 1;"; 55 | char *request_uri_template = "http://query.yahooapis.com/v1/public/yql?q=%s&format=json"; 56 | json_t *weather_data; 57 | json_error_t error; 58 | struct weather *weather = calloc(1, sizeof(struct weather)); 59 | CURL *curl; 60 | 61 | curl = curl_easy_init(); 62 | 63 | query_str_len = snprintf(NULL, 0, query_str_template, location->city, location->country_code); 64 | query_str = malloc(query_str_len + 1); 65 | snprintf(query_str, query_str_len + 1, query_str_template, location->city, location->country_code); 66 | 67 | query_str_escaped = curl_easy_escape(curl, query_str, 0); 68 | request_uri_len = snprintf(NULL, 0, request_uri_template, query_str_escaped); 69 | request_uri = malloc(request_uri_len + 1); 70 | snprintf(request_uri, request_uri_len + 1, request_uri_template, query_str_escaped); 71 | 72 | char *weather_raw_json = candybar_curl_request(request_uri); 73 | weather_data = json_loads(weather_raw_json, 0, &error); 74 | 75 | free(query_str); 76 | query_str = NULL; 77 | free(query_str_escaped); 78 | query_str_escaped = NULL; 79 | free(request_uri); 80 | request_uri = NULL; 81 | curl_easy_cleanup(curl); 82 | curl_global_cleanup(); 83 | 84 | if (!weather_data) { 85 | goto error; 86 | } 87 | 88 | free(weather_raw_json); 89 | weather_raw_json = NULL; 90 | 91 | json_t *weather_code, *weather_temp; 92 | json_t *tmp_obj = NULL; 93 | tmp_obj = json_object_get(weather_data, "query"); 94 | tmp_obj = json_object_get(tmp_obj, "results"); 95 | tmp_obj = json_object_get(tmp_obj, "channel"); 96 | tmp_obj = json_object_get(tmp_obj, "item"); 97 | tmp_obj = json_object_get(tmp_obj, "condition"); 98 | if (!json_is_object(tmp_obj)) { 99 | LOG_ERR("invalid weather data object received"); 100 | goto error; 101 | } 102 | 103 | weather_code = json_object_get(tmp_obj, "code"); 104 | weather_temp = json_object_get(tmp_obj, "temp"); 105 | 106 | if (!json_is_string(weather_code) || !json_is_string(weather_temp)) { 107 | LOG_ERR("invalid weather query result received (weather code or temp missing)"); 108 | if (tmp_obj != NULL) { 109 | json_decref(tmp_obj); 110 | } 111 | goto error; 112 | } 113 | 114 | int int_val; 115 | char *end; 116 | 117 | int_val = strtol(json_string_value(weather_code), &end, 10); 118 | if (*end) { 119 | LOG_WARN("received weather code is not an integer"); 120 | } 121 | else { 122 | weather->code = int_val; 123 | } 124 | 125 | int_val = strtol(json_string_value(weather_temp), &end, 10); 126 | if (*end) { 127 | LOG_WARN("received temperature is not an integer"); 128 | } 129 | else { 130 | weather->temp = int_val; 131 | } 132 | 133 | if (tmp_obj != NULL) { 134 | json_decref(tmp_obj); 135 | } 136 | if (weather_data != NULL) { 137 | json_decref(weather_data); 138 | } 139 | 140 | return weather; 141 | 142 | error: 143 | json_decref(weather_data); 144 | 145 | free(weather); 146 | weather = NULL; 147 | 148 | return NULL; 149 | } 150 | 151 | static int 152 | widget_update (struct widget *widget, struct location *location, struct widget_config config) { 153 | struct weather *weather; 154 | 155 | weather = get_weather_information(location); 156 | if (!weather) { 157 | LOG_ERR("error while fetching weather data"); 158 | 159 | return -1; 160 | } 161 | 162 | widget_data_callback(widget, 163 | widget_data_arg_number(weather->code), 164 | widget_data_arg_number(weather->temp), 165 | widget_data_arg_string(config.unit)); 166 | 167 | free(weather); 168 | weather = NULL; 169 | 170 | return 0; 171 | } 172 | 173 | void* 174 | widget_main (struct widget *widget) { 175 | struct widget_config config = widget_config_defaults; 176 | widget_init_config_string(widget->config, "location", config.location); 177 | widget_init_config_string(widget->config, "unit", config.unit); 178 | widget_init_config_integer(widget->config, "refresh_interval", config.refresh_interval); 179 | widget_epoll_init(widget); 180 | 181 | struct location *location = calloc(1, sizeof(location)); 182 | 183 | if (strlen(config.location) > 0) { 184 | location->city = strdup(config.location); 185 | location->country_code = strdup(""); 186 | } 187 | else { 188 | get_geoip_location(location); 189 | } 190 | if (!(location->city || location->country_code)) { 191 | LOG_WARN("could not get GeoIP location, consider setting the location manually in config.json"); 192 | goto cleanup; 193 | } 194 | 195 | while (true) { 196 | widget_update(widget, location, config); 197 | widget_epoll_wait_goto(widget, config.refresh_interval, cleanup); 198 | } 199 | 200 | cleanup: 201 | free(location->city); 202 | location->city = NULL; 203 | 204 | free(location->country_code); 205 | location->country_code = NULL; 206 | 207 | free(location); 208 | location = NULL; 209 | 210 | widget_epoll_cleanup(widget); 211 | widget_clean_exit(widget); 212 | } 213 | -------------------------------------------------------------------------------- /src/widgets/weather.h: -------------------------------------------------------------------------------- 1 | #include "util/curl.h" 2 | 3 | struct location { 4 | char *city; 5 | char *country_code; 6 | }; 7 | 8 | struct weather { 9 | int code; 10 | double temp; 11 | char *unit; 12 | }; 13 | 14 | static struct widget_config { 15 | const char *location; 16 | const char *unit; 17 | int refresh_interval; 18 | } widget_config_defaults = { 19 | .location = "", 20 | .unit = "c", 21 | .refresh_interval = 1800, 22 | }; 23 | -------------------------------------------------------------------------------- /src/widgets/window_title.c: -------------------------------------------------------------------------------- 1 | #include "widgets.h" 2 | #include "window_title.h" 3 | 4 | static int 5 | widget_update (struct widget *widget, xcb_ewmh_connection_t *ewmh, int screen_nbr, xcb_window_t *cur_win) { 6 | xcb_window_t win; 7 | xcb_ewmh_get_utf8_strings_reply_t ewmh_txt_prop; 8 | xcb_icccm_get_text_property_reply_t icccm_txt_prop; 9 | uint32_t values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; 10 | uint32_t values_reset[] = { XCB_EVENT_MASK_NO_EVENT }; 11 | char window_title[BUFSIZ]; 12 | 13 | ewmh_txt_prop.strings = NULL; 14 | icccm_txt_prop.name = NULL; 15 | 16 | if ((xcb_ewmh_get_active_window_reply(ewmh, xcb_ewmh_get_active_window(ewmh, screen_nbr), &win, NULL) == 1) 17 | && ((xcb_ewmh_get_wm_name_reply(ewmh, xcb_ewmh_get_wm_name(ewmh, win), &ewmh_txt_prop, NULL) == 1) 18 | || (xcb_icccm_get_wm_name_reply(ewmh->connection, xcb_icccm_get_wm_name(ewmh->connection, win), &icccm_txt_prop, NULL) == 1))) { 19 | if ((ewmh_txt_prop.strings != NULL) && (ewmh_txt_prop.strings_len > 0)) { 20 | copy_prop(window_title, ewmh_txt_prop.strings, ewmh_txt_prop.strings_len, 0, 1); 21 | } 22 | else if ((icccm_txt_prop.name != NULL) && (icccm_txt_prop.name_len > 0)) { 23 | copy_prop(window_title, icccm_txt_prop.name, icccm_txt_prop.name_len, 0, 1); 24 | } 25 | else { 26 | strcpy(window_title, MISSING_VALUE); 27 | } 28 | if (win != *cur_win) { 29 | xcb_change_window_attributes(ewmh->connection, *cur_win, XCB_CW_EVENT_MASK, values_reset); 30 | cur_win = &win; 31 | } 32 | xcb_generic_error_t *err = xcb_request_check(ewmh->connection, xcb_change_window_attributes_checked(ewmh->connection, win, XCB_CW_EVENT_MASK, values)); 33 | if (err != NULL) { 34 | LOG_INFO("could not capture property change events on window 0x%X", win); 35 | } 36 | } 37 | else { 38 | strcpy(window_title, MISSING_VALUE); 39 | } 40 | 41 | widget_data_callback(widget, widget_data_arg_string(window_title)); 42 | 43 | return 0; 44 | } 45 | 46 | void* 47 | widget_main (struct widget *widget) { 48 | unsigned short i; 49 | int xcb_fd; 50 | int screen_nbr = 0; 51 | xcb_window_t cur_win = 0; 52 | xcb_connection_t *conn = xcb_connect(NULL, NULL); 53 | xcb_ewmh_connection_t *ewmh = malloc(sizeof(xcb_ewmh_connection_t)); 54 | struct epoll_event xcb_event; 55 | 56 | widget_epoll_init(widget); 57 | 58 | if (xcb_connection_has_error(conn)) { 59 | LOG_ERR("could not connect to display %s", getenv("DISPLAY")); 60 | goto cleanup; 61 | } 62 | 63 | xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(conn, ewmh); 64 | xcb_ewmh_init_atoms_replies(ewmh, ewmh_cookie, NULL); 65 | 66 | uint32_t values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; 67 | xcb_generic_event_t *evt; 68 | xcb_generic_error_t *err = xcb_request_check(ewmh->connection, 69 | xcb_change_window_attributes_checked(ewmh->connection, 70 | ewmh->screens[screen_nbr]->root, 71 | XCB_CW_EVENT_MASK, 72 | values)); 73 | 74 | if (err != NULL) { 75 | LOG_ERR("could not request EWMH property change notifications"); 76 | goto cleanup; 77 | } 78 | 79 | xcb_fd = xcb_get_file_descriptor(ewmh->connection); 80 | xcb_event.data.fd = xcb_fd; 81 | xcb_event.events = EPOLLIN | EPOLLET; 82 | if (epoll_ctl(efd, EPOLL_CTL_ADD, xcb_fd, &xcb_event) == -1) { 83 | LOG_ERR("failed to add fd to epoll instance: %s", strerror(errno)); 84 | 85 | return 0; 86 | } 87 | 88 | widget_update(widget, ewmh, screen_nbr, &cur_win); 89 | while (true) { 90 | while ((nfds = epoll_wait(efd, events, MAX_EVENTS, -1)) > 0) { 91 | for (i = 0; i < nfds; i++) { 92 | if (events[i].data.fd == widget->bar->efd) { 93 | goto cleanup; 94 | } 95 | } 96 | 97 | while ((evt = xcb_poll_for_event(ewmh->connection)) != NULL) { 98 | xcb_property_notify_event_t *pne; 99 | switch (XCB_EVENT_RESPONSE_TYPE(evt)) { 100 | case XCB_PROPERTY_NOTIFY: 101 | pne = (xcb_property_notify_event_t*)evt; 102 | if (pne->atom == ewmh->_NET_ACTIVE_WINDOW) { 103 | widget_update(widget, ewmh, screen_nbr, &cur_win); 104 | } 105 | else if ((pne->window != ewmh->screens[screen_nbr]->root) && ((pne->atom == ewmh->_NET_WM_NAME) || (pne->atom == XCB_ATOM_WM_NAME))) { 106 | widget_update(widget, ewmh, screen_nbr, &cur_win); 107 | } 108 | default: 109 | break; 110 | } 111 | free(evt); 112 | } 113 | } 114 | } 115 | 116 | cleanup: 117 | if (ewmh != NULL) { 118 | xcb_ewmh_connection_wipe(ewmh); 119 | } 120 | if (conn != NULL) { 121 | xcb_disconnect(conn); 122 | } 123 | 124 | widget_epoll_cleanup(widget); 125 | widget_clean_exit(widget); 126 | } 127 | -------------------------------------------------------------------------------- /src/widgets/window_title.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lokaltog/candybar/e81b66abe3c2ad96d31c670fcf8b3717c96fe4a2/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import subprocess 5 | 6 | from waflib import Utils, TaskGen 7 | 8 | TaskGen.declare_chain( 9 | rule='${A2X} --doctype manpage --format manpage ${SRC}', 10 | ext_in='.asciidoc', 11 | ext_out='', 12 | ) 13 | 14 | PACKAGE = 'candybar' 15 | LIBDIR = '${{PREFIX}}/lib/{}'.format(PACKAGE) 16 | 17 | 18 | def get_version(): 19 | '''Attempt to fetch the current version number from git.''' 20 | version = 'beta' 21 | if not os.path.exists('.git'): 22 | return version + '-unknown' 23 | version += '-git-' + subprocess.Popen('git rev-list --count HEAD', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').strip() 24 | version += '.' + subprocess.Popen('git rev-parse --short HEAD', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').strip() 25 | return version 26 | 27 | 28 | def options(opt): 29 | opt.load('compiler_c') 30 | opt.add_option('--confdir', dest='confdir', default='/etc/xdg/{}'.format(PACKAGE), help='directory to store {} global configuration files [default: %default]'.format(PACKAGE)) 31 | opt.add_option('--debug', dest='debug', default=False, action='store_true', help='build debug version') 32 | opt.add_option('--rootdir', dest='rootdir', default='/', help='root directory override (useful for development) [default: %default]') 33 | opt.add_option('--theme', dest='theme', default='https://github.com/Lokaltog/{}-theme-default/archive/gh-pages.tar.gz'.format(PACKAGE), help='default theme to install locally [default: %default]') 34 | opt.add_option('--themedir', dest='themedir', default='${{PREFIX}}/share/{}/theme-default'.format(PACKAGE), help='destination directory for default theme [default: %default]') 35 | 36 | 37 | def configure(ctx): 38 | ctx.load('compiler_c') 39 | ctx.check_cfg(atleast_pkgconfig_version='0.0.0') 40 | 41 | # compiler options 42 | if ctx.options.debug: 43 | ctx.env.append_unique('CFLAGS', ['-O0', '-g3', '-ggdb', '-Wall', '-Wpedantic', '-Wextra', '-Wno-unused-parameter', '-std=c99']) 44 | ctx.env.append_unique('DEFINES', 'DEBUG') 45 | else: 46 | ctx.env.append_unique('CFLAGS', ['-O3', '-g', '-Werror', '-Wall', '-Wpedantic', '-Wextra', '-Wno-unused-parameter', '-std=c99']) 47 | ctx.env.append_unique('DEFINES', 'RELEASE') 48 | ctx.env.append_unique('INCLUDES', ['./src']) 49 | ctx.env.append_unique('DEFINES', '_POSIX_C_SOURCE=200809L') 50 | 51 | # defines 52 | ctx.define('PACKAGE', PACKAGE) 53 | ctx.define('CONFDIR', ctx.options.confdir) 54 | ctx.define('LIBDIR', Utils.subst_vars(os.path.join(ctx.options.rootdir, LIBDIR), ctx.env)) 55 | 56 | # various build deps 57 | ctx.find_program('a2x', var='A2X', mandatory=False) 58 | ctx.find_program('wget', var='WGET', mandatory=False) 59 | ctx.find_program('tar', var='TAR', mandatory=False) 60 | 61 | # deps 62 | ctx.check_cfg(package='gtk+-3.0', uselib_store='GTK', args=['--cflags', '--libs']) 63 | ctx.check_cfg(package='glib-2.0 gmodule-2.0', uselib_store='GLIB', args=['--cflags', '--libs']) 64 | ctx.check_cfg(package='webkitgtk-3.0', uselib_store='WEBKITGTK', args=['--cflags', '--libs']) 65 | ctx.check_cfg(package='jansson', uselib_store='JANSSON', args=['--cflags', '--libs']) 66 | 67 | # optdeps 68 | ctx.check_cfg(package='alsa', uselib_store='ALSA', args=['--cflags', '--libs'], mandatory=False) 69 | ctx.check_cfg(package='libcurl', uselib_store='CURL', args=['--cflags', '--libs'], mandatory=False) 70 | ctx.check_cfg(package='dbus-1 dbus-glib-1', uselib_store='DBUS', args=['--cflags', '--libs'], mandatory=False) 71 | ctx.check_cfg(package='GraphicsMagickWand', uselib_store='MAGICK', args=['--cflags', '--libs'], mandatory=False) 72 | ctx.check_cfg(package='libmpdclient', uselib_store='LIBMPDCLIENT', args=['--cflags', '--libs'], mandatory=False) 73 | ctx.check_cfg(package='playerctl-1.0', uselib_store='PLAYERCTL', args=['--cflags', '--libs'], mandatory=False) 74 | ctx.check_cfg(package='xcb-util xcb-ewmh xcb-icccm', uselib_store='XCB', args=['--cflags', '--libs'], mandatory=False) 75 | ctx.check_cfg(package='i3ipc-glib-1.0', uselib_store='I3IPC', args=['--cflags', '--libs'], mandatory=False) 76 | 77 | 78 | def build(bld): 79 | basedeps = ['GTK', 'GLIB', 'WEBKITGTK', 'JANSSON'] 80 | 81 | # add build version/time defines 82 | package_defines = [ 83 | 'VERSION="{0}"'.format(get_version()), 84 | ] 85 | 86 | bld.objects(source=bld.path.ant_glob('src/util/(log|config|copy_prop|gdk_helpers|process).c'), target='baseutils', use=basedeps) 87 | bld.objects(source='src/util/process.c', target='util_process', use=basedeps, cflags=['-fPIC']) 88 | 89 | # widgets 90 | bld.shlib(source='src/widgets/datetime.c', target='widget_datetime', use=basedeps, install_path=LIBDIR) 91 | 92 | if bld.is_defined('HAVE_ALSA'): 93 | bld.shlib(source='src/widgets/volume.c', target='widget_volume', use=basedeps + ['ALSA'], install_path=LIBDIR) 94 | 95 | if bld.is_defined('HAVE_CURL'): 96 | bld.objects(source='src/util/curl.c', target='util_curl', use=basedeps + ['CURL'], cflags=['-fPIC']) 97 | 98 | bld.shlib(source='src/widgets/email_imap.c', target='widget_email_imap', use=basedeps + ['CURL', 'util_curl', 'util_process'], install_path=LIBDIR) 99 | bld.shlib(source='src/widgets/external_ip.c', target='widget_external_ip', use=basedeps + ['CURL', 'util_curl'], install_path=LIBDIR) 100 | bld.shlib(source='src/widgets/weather.c', target='widget_weather', use=basedeps + ['CURL', 'util_curl'], install_path=LIBDIR) 101 | 102 | if bld.is_defined('HAVE_DBUS'): 103 | bld.objects(source='src/util/dbus_helpers.c', target='util_dbus_helpers', use=basedeps + ['DBUS'], cflags=['-fPIC']) 104 | 105 | bld.shlib(source='src/widgets/battery.c', target='widget_battery', use=basedeps + ['DBUS', 'util_dbus_helpers'], install_path=LIBDIR) 106 | bld.shlib(source='src/widgets/notifications.c', target='widget_notifications', use=basedeps + ['DBUS', 'util_dbus_helpers'], install_path=LIBDIR) 107 | 108 | if bld.is_defined('HAVE_MAGICK'): 109 | bld.shlib(source='src/widgets/magick_background.c', target='widget_magick_background', use=basedeps + ['MAGICK'], install_path=LIBDIR) 110 | 111 | if bld.is_defined('HAVE_LIBMPDCLIENT'): 112 | bld.shlib(source='src/widgets/now_playing_mpd.c', target='widget_now_playing_mpd', use=basedeps + ['LIBMPDCLIENT'], install_path=LIBDIR) 113 | 114 | if bld.is_defined('HAVE_PLAYERCTL'): 115 | bld.shlib(source='src/widgets/now_playing_mpris.c', target='widget_now_playing_mpris', use=basedeps + ['PLAYERCTL'], install_path=LIBDIR) 116 | 117 | if bld.is_defined('HAVE_XCB'): 118 | bld.shlib(source='src/widgets/desktops.c', target='widget_desktops', use=basedeps + ['XCB'], install_path=LIBDIR) 119 | bld.shlib(source='src/widgets/window_title.c', target='widget_window_title', use=basedeps + ['util_copy_prop', 'XCB'], install_path=LIBDIR) 120 | 121 | if bld.is_defined('HAVE_I3IPC'): 122 | bld.shlib(source='src/widgets/desktops_i3.c', target='widget_desktops_i3', use=basedeps + ['I3IPC'], install_path=LIBDIR) 123 | 124 | bld.objects(source='src/widgets.c', target='widgets', use=['baseutils'] + basedeps) 125 | bld.program(source='src/{}.c'.format(PACKAGE), target=PACKAGE, use=['baseutils', 'widgets'] + basedeps, defines=package_defines) 126 | 127 | # man pages 128 | for manpage in [1, 5]: 129 | if bld.env.A2X: 130 | bld(source='docs/{}.{}.asciidoc'.format(PACKAGE, manpage)) 131 | bld.install_files('${{PREFIX}}/share/man/man{}'.format(manpage), 'docs/{}.{}'.format(PACKAGE, manpage)) 132 | else: 133 | bld.install_files('${{PREFIX}}/share/doc/{}'.format(PACKAGE), 'docs/{}.{}.asciidoc'.format(PACKAGE, manpage)) 134 | 135 | # default theme 136 | use_remote_theme = True 137 | if bld.env.WGET and bld.env.TAR: 138 | bld( 139 | name='download_theme', 140 | rule='${{WGET}} {} -q -O ${{TGT}}'.format(bld.options.theme), 141 | target='theme.tar.gz', 142 | ) 143 | bld( 144 | name='extract_theme', 145 | after='download_theme', 146 | rule='mkdir -p ${TGT} && ${TAR} -xz --strip-components 1 -C ${TGT} -f ${SRC}', 147 | source='theme.tar.gz', 148 | target='theme', 149 | ) 150 | 151 | theme_dir = bld.path.get_bld().make_node('theme') 152 | 153 | def read_theme_files(task): 154 | # update theme file signatures 155 | for f in theme_dir.ant_glob('**'): 156 | f.sig = Utils.h_file(f.abspath()) 157 | 158 | bld( 159 | after='extract_theme', 160 | rule=read_theme_files, 161 | always=True, 162 | ) 163 | 164 | bld.install_files( 165 | bld.options.themedir, 166 | theme_dir.find_resource('index.html') 167 | ) 168 | 169 | use_remote_theme = False 170 | 171 | def subst_theme_uri(task, text): 172 | return text.replace( 173 | '@THEME_URI@', 174 | 'http://lokaltog.github.io/candybar-theme-default/' if use_remote_theme 175 | else 'file://' + Utils.subst_vars(os.path.join(bld.options.rootdir, bld.options.themedir, 'index.html'), bld.env) 176 | ) 177 | 178 | bld( 179 | features='subst', 180 | subst_fun=subst_theme_uri, 181 | source='src/config.json', 182 | target='config.json', 183 | ) 184 | 185 | # install default config 186 | bld.install_files(bld.options.confdir, ['config.json']) 187 | --------------------------------------------------------------------------------