diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..2a12de2 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,9 @@ +Developed by: + Thierry Lorthiois , project maintainer + Pål Staurland , tint2 is based on ttm originally written by Pål Staurland + + +Contributors: + Daniel Moerner , man page + + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ffde567 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,252 @@ + + +2008-09-30 +- change tintrc format + background and border can use linear gradiant +- update documentation + +2008-09-24 +- change tintrc format + user can define a list of background and border at the beginning of tintrc + and each object (panel, taskbar, task, clock, systray) get a background number. + the background number 0 is reserved by tint2 for full transparency. + user's background start with number 1, 2, ... + +2008-09-22 +- better control on icon opacity + icon opacity use the same value as text opacity + +2008-09-21 +- new parameter task_text = 0 or 1 + so you can show task's icon without text +- remove task_icon_size parameter because icon_size is calculate with task->padding_y + +2008-09-19 +- improve clock drawing on 2 lines +- new design : object oriented + clock inherit Area (with draw_background) +- rename parameter 'panel_mode' in 'taskbar_mode' + +2008-09-17 +- change name from tint to tint2 because debian has already a package named tint +- new design : object oriented + tasks inherit Area + taskbars inherit Area + +2008-09-16 +- new design : object oriented + remove task_margin parameter, replaced by taskbar_padding + fixed segfault with new layout + +2008-09-04 +- new design : object oriented + all objects inherit from Area : panel, taskbar, task, clock + +2008-09-02 licence changed to GPL V2 +> Hi Pål, +> Since latest release (tint-0.6), I try to merge a systray in tint. +> The code I would like to merge is under GPL V2 licence. +> So are you agree to change the licence of tint to GPL V2 ? +Pål Staurland answer : + Yeah no problem::) + +2008-08-17 patch from i...@noctus.net +- fixed bug : "Task button titles do not update" + +2008-08-12 +- code cleanup : Taskbar use GSLIST from glib + +2008-08-07 +- add 1 space between task title and icon + +2008-07-25 +- fixed bug when (task_rounded > panel_height/2) + + +---------------------------------------------------------------- +2008-07-20 thil7 +released tint-0.6 + +2008-07-19 +- backward compatibility : save new config file + +2008-07-18 +- bug fixed with multi-monitor : memory leak, move task on the same monitor + +2008-07-12 +- multi_monitor : added window_get_monitor(win) + +2008-07-05 +- multi_monitor : changed taskbar allocation when add/remove a monitor +- update documentation + +2008-07-04 +- changed in config file, panel_show_all_desktop + replaced by panel_mode = single_desktop / multi_desktop / multi_monitor +- with panel_mode = multi_monitor, tint show one taskbar by monitor + +2008-07-02 +- add xinerama dependency +- read monitors configuration +- tint will run in 'sleep_mode' + if you specify 'panel_monitor' on an unplug monitor +- tint 'wake up' when the monitor is detected or resolution changed + +2008-06-30 patch from goo...@dougbarton.us +- tint wait for window manager on startup + +2008-06-29 +- specify monitor with parameter 'panel_monitor' +- panel_position is relative to your monitor + panel_monitor = 1 + panel_position = bottom center + +2008-06-24 +- fixed bug : show tint on all desktop with fluxbox + +2008-06-23 +- task_width = 0 to get full taskbar width +- added documentation in /doc + +2008-06-16 +- renamed parameter panel_margin to panel_padding +- added parameter panel_margin = vertical_margin horizontal_margin + +2008-06-15 +- fixed bug : icon position changed when clock adjust width +- removed boolean parameter panel_background and task_background + replaced with opacity (alpha) = 0 +- removed task_active_border_width parameter + identical to task_border_width +- fixed bug : draw task below panel border + +2008-06-11 +- removed the need of desktop margin. + tint specify margin to window magager (_NET_WM_STRUT) + +2008-06-10 +- fixed bug : time/date vertical and horizontal center + +2008-06-10 patch from benjaminfranzke +- fixed bug : draw icon on 64bit system. + +2008-06-08 +- fixed bug : in draw border and fill background + +2008-06-04 +- allow config file on the command line : tint -c ./tintrc2 +- allow 2 value in config file : key = value1 value2 +- panel_margin can get 2 values : vertical_margin horizontal_margin + panel_margin = 8 4 + +2008-05-30 +- put panel below other window +- set locale LANG + +2008-05-28 +- clock_width adjust automatically +- configure : time2_format see 'man strftime' + +2008-05-27 +- configure : time1_format see 'man strftime' +- cleanup code : refresh_pos() + +2008-05-26 +- catch time event in main loop +- draw clock fixed format %H:%M + +2008-05-24 +- removed boolean parameter task_icon + task_icon_size = 0 replace task_icon = 0 +- all colors parameters can get 2 values : color and opacity (alpha) + font_color = #ffffff 70 + +2008-05-23 +- cpu optimisation : limit call to visual_refresh() + +2008-05-22 +- configure clock : clock_font, clock_font_color + + +---------------------------------------------------------------- +2008-05-20 thil7 +released tint-0.5 + +2008-05-19 +- improve usability in mode 'show_all_desktop' + -> don't switch desktop when close a task of another desktop + -> allow to drag and drop task between desktop +- change panel_position : you can now specify top or bottom. + values : bottom left, bottom right, bottom center, top left, top right, top center + keep compatibility with old tintrc files +- change Makefile : + add $PREFIX and $DESTDIR + install default config file in /etc/xdg/tint/tintrc +- on startup, tint copy $XDG_CONFIG_DIR/tint/tintrc in ~/.config/tint/tintrc + so each user can customize tintrc +- fixed : name of task in fluxbox +- improve focus detection (TransientForHint) +- cleanup code : send_event + + +---------------------------------------------------------------- +2008-05-14 thil7 +released tint-0.4 + +2008-05-12 +- boolean option 'show_all_desktop' + 0 : tint panel show one taskbar (from current desktop) + 1 : tint panel show all taskbars (from all desktops) + - clic on a task switch to the desktop + - clic on a taskbar (if empty) switch to the desktop +- add layout for taskbar : panel -> taskbar -> task +- cleanup code : state 'active' in Task replaced by task_active in Panel +- hide some window : _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_STATE_MODAL +- change project to 'MIT licence' + because Pål Staurland put 'MIT licence' in all file source + + +---------------------------------------------------------------- +2008-05-10 thil7 +released tint-0.3 + +2008-05-10 +- manage events : 'send to desktop' and 'send to all desktop' +- bug solved : 'close' action now send _NET_CLOSE_WINDOW + so openoffice ask 'save document...' if needed +- cpu optimisation : + -> don't redraw panel background on all events. just when desktop change. + -> don't remove/add all tasks when switch between desktop. remove 'task_new_desktop()'. +- small bug on _NET_WM_STATE_SKIP_TASKBAR corrected +- memory leak on g_build_filename + + +---------------------------------------------------------------- +2008-05-09 thil7 +released tint-0.2 + +2008-05-08 +- panel : add rounded and border (color and alpha) +- remove option 'panel_tasks_centered' +- user can configure mouse event on task + (none, toggle, iconify, shade, close, toggle_iconify) + +2008-05-07 +- cpu optimisation : resize_tasks only when add/remove task, + compute pos_y at startup + +2008-05-04 +- task width adjust when too many task + +2008-05-01 +- task : add rounded and border (color and alpha) +- panel position (left, right, center) thank to ak47izatool +- cleanup code : vertical position (icon and text), + better refresh after delete task, memory leak *font +- panel : changed panel_margin and task_margin + +2008-04-22 +- fork ttm projet from p://code.google.com/p/ttm/ (by Pål Staurland staura@gmail.com) + while the projet is no longer in developpement, have not changed the name of 'tint'. + + diff --git a/README b/README new file mode 100644 index 0000000..a250d34 --- /dev/null +++ b/README @@ -0,0 +1,18 @@ +DEPENDENCIES: +cairo, pango, glib, imlib2 + +--------------------------------------------------------- +INSTALL: + +cd to "src" and type "make" +as root type "make install" + +execute "tint2" +or "tint2 -c path_to_config_file" + +documentation is in /doc + +check http://code.google.com/p/tint2/ +for latest release, documentation and sample config file. + + diff --git a/doc/man/tint2.1 b/doc/man/tint2.1 new file mode 100644 index 0000000..ce4646b --- /dev/null +++ b/doc/man/tint2.1 @@ -0,0 +1,50 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH TINT 1 "2008-09-05" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +tint \- lightweight taskbar +.SH SYNOPSIS +.B tint +.br +.B tint +.RI -c +.IR /path/to/config/file +.SH DESCRIPTION +This manual page documents briefly the +.B tint +command. +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBtint\fP is a simple panel/taskbar intentionally made for openbox3, but +should also work with other window managers. It's based on ttm code. The goal is to keep a clean and unintrusive look with code lightweight and +compliance with freedesktop specifications. +.PP +On the first startup tint creates a config file in $HOME/.config/tint/tintrc. +See the /usr/share/tint2/doc directory for more information. +.SH OPTIONS +.TP +.B \-c config-file +Specify which configuration file to use instead of the default. +.SH AUTHOR +tint was written by Thierry Lorthiois . It is based on +ttm, originally written by Pål Staurland +.PP +This manual page was written by Daniel Moerner , +for the Debian project (but may be used by others). It was adopted from the +tint docs. diff --git a/doc/tint2-0.7.odt b/doc/tint2-0.7.odt new file mode 100644 index 0000000..6a99fbb Binary files /dev/null and b/doc/tint2-0.7.odt differ diff --git a/doc/tint2-0.7.pdf b/doc/tint2-0.7.pdf new file mode 100644 index 0000000..873effd Binary files /dev/null and b/doc/tint2-0.7.pdf differ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..3eecbbb --- /dev/null +++ b/src/Makefile @@ -0,0 +1,36 @@ +#CFLAGS="-O2" +FLAGS=-Wall -g `pkg-config --cflags --libs cairo pangocairo x11 xinerama imlib2 glib-2.0` +PROGNAME=tint2 +FILES=tint.c server.c panel.c config.c taskbar/task.c taskbar/taskbar.c clock/clock.c systray/docker.c systray/icons.c systray/kde.c systray/net.c systray/xproperty.c util/window.c util/area.c + +ifndef DESTDIR + ifndef PREFIX + BINDIR=/usr/bin + XDG_CONFIG_DIR=/etc/xdg + else + BINDIR=$(PREFIX)/bin + XDG_CONFIG_DIR=/etc/xdg + endif +else + BINDIR=$(DESTDIR)/usr/bin + XDG_CONFIG_DIR=$(DESTDIR)/etc/xdg +endif + + +$(PROGNAME): $(FILES) $(SYSTRAYOBJ) + $(CC) $(CFLAGS) $(FLAGS) -I. -Iutil -Iclock -Itaskbar -Isystray -o $(PROGNAME) $(FILES) + strip $(PROGNAME) + +install: + mkdir -p $(BINDIR) + mkdir -p $(XDG_CONFIG_DIR)/tint2 + install $(PROGNAME) $(BINDIR) + cp -f ../tintrc06 $(XDG_CONFIG_DIR)/tint2/tint2rc + cp -f ../doc/man/tint2.1 /usr/man/man1 + +uninstall: + rm -f $(BINDIR)/$(PROGNAME) + +clean: + rm -f $(PROGNAME) + diff --git a/src/clock/clock.c b/src/clock/clock.c new file mode 100644 index 0000000..f91e969 --- /dev/null +++ b/src/clock/clock.c @@ -0,0 +1,126 @@ +/************************************************************************** +* +* Tint2 : clock +* +* Copyright (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include + +#include "window.h" +#include "server.h" +#include "area.h" +#include "clock.h" + + +void init_clock(Clock *clock, int panel_height) +{ + char buf_time[40]; + char buf_date[40]; + int time_height, time_height_ink, date_height, date_height_ink; + + if (!clock->time1_format) return; + + if (strchr(clock->time1_format, 'S') == NULL) clock->time_precision = 60; + else clock->time_precision = 1; + + gettimeofday(&clock->clock, 0); + clock->clock.tv_sec -= clock->clock.tv_sec % clock->time_precision; + + strftime(buf_time, sizeof(buf_time), clock->time1_format, localtime(&clock->clock.tv_sec)); + if (clock->time2_format) + strftime(buf_date, sizeof(buf_date), clock->time2_format, localtime(&clock->clock.tv_sec)); + + get_text_size(clock->time1_font_desc, &time_height_ink, &time_height, panel_height, buf_time, strlen(buf_time)); + clock->time1_posy = (clock->area.height - time_height) / 2; + + if (clock->time2_format) { + get_text_size(clock->time2_font_desc, &date_height_ink, &date_height, panel_height, buf_date, strlen(buf_date)); + + clock->time1_posy -= ((date_height_ink + 2) / 2); + clock->time2_posy = clock->time1_posy + time_height + 2 - (time_height - time_height_ink)/2 - (date_height - date_height_ink)/2; + } +} + + +int draw_foreground_clock (void *obj, cairo_t *c) +{ + Clock *clock = obj; + PangoLayout *layout; + char buf_time[40]; + char buf_date[40]; + int time_width, date_width, new_width; + + time_width = date_width = 0; + strftime(buf_time, sizeof(buf_time), clock->time1_format, localtime(&clock->clock.tv_sec)); + if (clock->time2_format) + strftime(buf_date, sizeof(buf_date), clock->time2_format, localtime(&clock->clock.tv_sec)); + + layout = pango_cairo_create_layout (c); + + // check width + pango_layout_set_font_description (layout, clock->time1_font_desc); + pango_layout_set_indent(layout, 0); + pango_layout_set_text (layout, buf_time, strlen(buf_time)); + pango_layout_get_pixel_size (layout, &time_width, NULL); + if (clock->time2_format) { + pango_layout_set_font_description (layout, clock->time2_font_desc); + pango_layout_set_indent(layout, 0); + pango_layout_set_text (layout, buf_date, strlen(buf_date)); + pango_layout_get_pixel_size (layout, &date_width, NULL); + } + if (time_width > date_width) new_width = time_width; + else new_width = date_width; + new_width += (2*clock->area.paddingx) + (2*clock->area.border.width); + + if (new_width > clock->area.width || (new_width != clock->area.width && date_width > time_width)) { + printf("clock_width %d, new_width %d\n", clock->area.width, new_width); + clock->area.width = new_width; + + g_object_unref (layout); + return 1; + } + + // draw layout + pango_layout_set_font_description (layout, clock->time1_font_desc); + pango_layout_set_width (layout, clock->area.width * PANGO_SCALE); + pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); + pango_layout_set_text (layout, buf_time, strlen(buf_time)); + + cairo_set_source_rgba (c, clock->font.color[0], clock->font.color[1], clock->font.color[2], clock->font.alpha); + + pango_cairo_update_layout (c, layout); + cairo_move_to (c, 0, clock->time1_posy); + pango_cairo_show_layout (c, layout); + + if (clock->time2_format) { + pango_layout_set_font_description (layout, clock->time2_font_desc); + pango_layout_set_indent(layout, 0); + pango_layout_set_text (layout, buf_date, strlen(buf_date)); + pango_layout_set_width (layout, clock->area.width * PANGO_SCALE); + + pango_cairo_update_layout (c, layout); + cairo_move_to (c, 0, clock->time2_posy); + pango_cairo_show_layout (c, layout); + } + + g_object_unref (layout); + return 0; +} + diff --git a/src/clock/clock.h b/src/clock/clock.h new file mode 100644 index 0000000..0d3e9e1 --- /dev/null +++ b/src/clock/clock.h @@ -0,0 +1,39 @@ +/************************************************************************** +* clock : +* - draw clock, adjust width +* +**************************************************************************/ + +#ifndef CLOCK_H +#define CLOCK_H + +#include +#include "common.h" +#include "area.h" + + +typedef struct Clock { + // -------------------------------------------------- + // always start with area + Area area; + + config_color font; + PangoFontDescription *time1_font_desc; + PangoFontDescription *time2_font_desc; + int time1_posy; + int time2_posy; + char *time1_format; + char *time2_format; + + struct timeval clock; + int time_precision; +} Clock; + + +// initialize clock : y position, precision, ... +void init_clock(Clock *clock, int panel_height); + +int draw_foreground_clock (void *obj, cairo_t *c); + + +#endif diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..c38cfec --- /dev/null +++ b/src/config.c @@ -0,0 +1,751 @@ +/************************************************************************** +* +* Tint2 : read/write config file +* +* Copyright (C) 2007 Pål Staurland (staura@gmail.com) +* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "server.h" +#include "task.h" +#include "taskbar.h" +#include "clock.h" +#include "panel.h" +#include "config.h" +#include "window.h" + + +void cleanup_taskbar() +{ + Task *tsk; + Taskbar *tskbar; + GSList *l0; + for (l0 = panel.area.list; l0 ; l0 = l0->next) { + tskbar = l0->data; + GSList *l1; + for (l1 = tskbar->area.list; l1 ; l1 = l1->next) { + tsk = l1->data; + remove_task (tsk); + } + g_slist_free(tskbar->area.list); + } + g_slist_free(panel.area.list); + panel.area.list = 0; +} + + +void cleanup () +{ + if (panel.old_task_font) free(panel.old_task_font); + if (g_task.font_desc) pango_font_description_free(g_task.font_desc); + if (panel.clock.time1_font_desc) pango_font_description_free(panel.clock.time1_font_desc); + if (panel.clock.time2_font_desc) pango_font_description_free(panel.clock.time2_font_desc); + cleanup_taskbar(); + if (panel.clock.time1_format) g_free(panel.clock.time1_format); + if (panel.clock.time2_format) g_free(panel.clock.time2_format); + if (server.monitor) free(server.monitor); + XCloseDisplay(server.dsp); +} + + +void copy_file(const char *pathSrc, const char *pathDest) +{ + FILE *fileSrc, *fileDest; + char line[100]; + int nb; + + fileSrc = fopen(pathSrc, "rb"); + if (fileSrc == NULL) return; + + fileDest = fopen(pathDest, "wb"); + if (fileDest == NULL) return; + + while ((nb = fread(line, 1, 100, fileSrc)) > 0) fwrite(line, 1, nb, fileDest); + + fclose (fileDest); + fclose (fileSrc); +} + + +void extract_values (const char *value, char **value1, char **value2) +{ + char *b; + + if (*value1) free (*value1); + if (*value2) free (*value2); + + if ((b = strchr (value, ' '))) { + b[0] = '\0'; + b++; + *value2 = strdup (b); + g_strstrip(*value2); + } + else *value2 = 0; + + *value1 = strdup (value); + g_strstrip(*value1); +} + + +int hex_char_to_int (char c) +{ + int r; + + if (c >= '0' && c <= '9') r = c - '0'; + else if (c >= 'a' && c <= 'f') r = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') r = c - 'A' + 10; + else r = 0; + + return r; +} + + +int hex_to_rgb (char *hex, int *r, int *g, int *b) +{ + int len; + + if (hex == NULL || hex[0] != '#') return (0); + + len = strlen (hex); + if (len == 3 + 1) { + *r = hex_char_to_int (hex[1]); + *g = hex_char_to_int (hex[2]); + *b = hex_char_to_int (hex[3]); + } + else if (len == 6 + 1) { + *r = hex_char_to_int (hex[1]) * 16 + hex_char_to_int (hex[2]); + *g = hex_char_to_int (hex[3]) * 16 + hex_char_to_int (hex[4]); + *b = hex_char_to_int (hex[5]) * 16 + hex_char_to_int (hex[6]); + } + else if (len == 12 + 1) { + *r = hex_char_to_int (hex[1]) * 16 + hex_char_to_int (hex[2]); + *g = hex_char_to_int (hex[5]) * 16 + hex_char_to_int (hex[6]); + *b = hex_char_to_int (hex[9]) * 16 + hex_char_to_int (hex[10]); + } + else return 0; + + return 1; +} + + +void get_color (char *hex, double *rgb) +{ + int r, g, b; + hex_to_rgb (hex, &r, &g, &b); + + rgb[0] = (r / 255.0); + rgb[1] = (g / 255.0); + rgb[2] = (b / 255.0); +} + + +void get_action (char *event, int *action) +{ + if (strcmp (event, "none") == 0) + *action = NONE; + else if (strcmp (event, "close") == 0) + *action = CLOSE; + else if (strcmp (event, "toggle") == 0) + *action = TOGGLE; + else if (strcmp (event, "iconify") == 0) + *action = ICONIFY; + else if (strcmp (event, "shade") == 0) + *action = SHADE; + else if (strcmp (event, "toggle_iconify") == 0) + *action = TOGGLE_ICONIFY; +} + + +void add_entry (char *key, char *value) +{ + char *value1=0, *value2=0; + + /* Background and border */ + if (strcmp (key, "rounded") == 0) { + // 'rounded' is the first parameter => alloc a new background + Area *back = calloc(1, sizeof(Area)); + back->border.rounded = atoi (value); + list_back = g_slist_append(list_back, back); + } + else if (strcmp (key, "border_width") == 0) { + Area *back = g_slist_last(list_back)->data; + back->border.width = atoi (value); + } + else if (strcmp (key, "background_color") == 0) { + Area *back = g_slist_last(list_back)->data; + extract_values(value, &value1, &value2); + get_color (value1, back->back.color); + if (value2) back->back.alpha = (atoi (value2) / 100.0); + else back->back.alpha = 0.5; + } + else if (strcmp (key, "border_color") == 0) { + Area *back = g_slist_last(list_back)->data; + extract_values(value, &value1, &value2); + get_color (value1, back->border.color); + if (value2) back->border.alpha = (atoi (value2) / 100.0); + else back->border.alpha = 0.5; + } + + /* Panel */ + else if (strcmp (key, "panel_monitor") == 0) { + panel.monitor = atoi (value); + if (panel.monitor > 0) panel.monitor -= 1; + } + else if (strcmp (key, "panel_size") == 0) { + extract_values(value, &value1, &value2); + panel.area.width = atoi (value1); + if (value2) panel.area.height = atoi (value2); + } + else if (strcmp (key, "panel_margin") == 0) { + extract_values(value, &value1, &value2); + panel.marginx = atoi (value1); + if (value2) panel.marginy = atoi (value2); + } + else if (strcmp (key, "panel_padding") == 0) { + extract_values(value, &value1, &value2); + panel.area.paddingx = atoi (value1); + if (value2) panel.area.paddingy = atoi (value2); + } + else if (strcmp (key, "panel_position") == 0) { + extract_values(value, &value1, &value2); + if (strcmp (value1, "top") == 0) panel.position = TOP; + else panel.position = BOTTOM; + + if (!value2) panel.position = CENTER; + else { + if (strcmp (value2, "left") == 0) panel.position |= LEFT; + else { + if (strcmp (value2, "right") == 0) panel.position |= RIGHT; + else panel.position |= CENTER; + } + } + } + else if (strcmp (key, "font_shadow") == 0) + g_task.font_shadow = atoi (value); + else if (strcmp (key, "panel_background_id") == 0) { + int id = atoi (value); + Area *back = g_slist_nth_data(list_back, id); + memcpy(&panel.area.back, &back->back, sizeof(Color)); + memcpy(&panel.area.border, &back->border, sizeof(Border)); + } + + /* Clock */ + else if (strcmp (key, "time1_format") == 0) { + if (panel.clock.time1_format) g_free(panel.clock.time1_format); + if (strlen(value) > 0) panel.clock.time1_format = strdup (value); + else panel.clock.time1_format = 0; + } + else if (strcmp (key, "time2_format") == 0) { + if (panel.clock.time2_format) g_free(panel.clock.time2_format); + if (strlen(value) > 0) panel.clock.time2_format = strdup (value); + else panel.clock.time2_format = 0; + } + else if (strcmp (key, "time1_font") == 0) { + if (panel.clock.time1_font_desc) pango_font_description_free(panel.clock.time1_font_desc); + panel.clock.time1_font_desc = pango_font_description_from_string (value); + } + else if (strcmp (key, "time2_font") == 0) { + if (panel.clock.time2_font_desc) pango_font_description_free(panel.clock.time2_font_desc); + panel.clock.time2_font_desc = pango_font_description_from_string (value); + } + else if (strcmp (key, "clock_font_color") == 0) { + extract_values(value, &value1, &value2); + get_color (value1, panel.clock.font.color); + if (value2) panel.clock.font.alpha = (atoi (value2) / 100.0); + else panel.clock.font.alpha = 0.1; + } + else if (strcmp (key, "clock_padding") == 0) { + extract_values(value, &value1, &value2); + panel.clock.area.paddingx = atoi (value1); + if (value2) panel.clock.area.paddingy = atoi (value2); + } + else if (strcmp (key, "clock_background_id") == 0) { + int id = atoi (value); + Area *back = g_slist_nth_data(list_back, id); + memcpy(&panel.clock.area.back, &back->back, sizeof(Color)); + memcpy(&panel.clock.area.border, &back->border, sizeof(Border)); + } + + /* Taskbar */ + else if (strcmp (key, "taskbar_mode") == 0) { + if (strcmp (value, "multi_desktop") == 0) panel.mode = MULTI_DESKTOP; + else if (strcmp (value, "multi_monitor") == 0) panel.mode = MULTI_MONITOR; + else panel.mode = SINGLE_DESKTOP; + } + else if (strcmp (key, "taskbar_padding") == 0) { + extract_values(value, &value1, &value2); + g_taskbar.paddingx = atoi (value1); + if (value2) g_taskbar.paddingy = atoi (value2); + } + else if (strcmp (key, "taskbar_background_id") == 0) { + int id = atoi (value); + Area *back = g_slist_nth_data(list_back, id); + memcpy(&g_taskbar.back, &back->back, sizeof(Color)); + memcpy(&g_taskbar.border, &back->border, sizeof(Border)); + } + + /* Task */ + else if (strcmp (key, "task_text") == 0) + g_task.text = atoi (value); + else if (strcmp (key, "task_icon") == 0) + g_task.icon = atoi (value); + else if (strcmp (key, "task_centered") == 0) + g_task.centered = atoi (value); + else if (strcmp (key, "task_width") == 0) + g_task.maximum_width = atoi (value); + else if (strcmp (key, "task_padding") == 0) { + extract_values(value, &value1, &value2); + g_task.area.paddingx = atoi (value1); + g_task.area_active.paddingx = atoi (value1); + if (value2) { + g_task.area.paddingy = atoi (value2); + g_task.area_active.paddingy = atoi (value2); + } + } + else if (strcmp (key, "task_font") == 0) { + if (g_task.font_desc) pango_font_description_free(g_task.font_desc); + g_task.font_desc = pango_font_description_from_string (value); + } + else if (strcmp (key, "task_font_color") == 0) { + extract_values(value, &value1, &value2); + get_color (value1, g_task.font.color); + if (value2) g_task.font.alpha = (atoi (value2) / 100.0); + else g_task.font.alpha = 0.1; + } + else if (strcmp (key, "task_active_font_color") == 0) { + extract_values(value, &value1, &value2); + get_color (value1, g_task.font_active.color); + if (value2) g_task.font_active.alpha = (atoi (value2) / 100.0); + else g_task.font_active.alpha = 0.1; + } + else if (strcmp (key, "task_background_id") == 0) { + int id = atoi (value); + Area *back = g_slist_nth_data(list_back, id); + memcpy(&g_task.area.back, &back->back, sizeof(Color)); + memcpy(&g_task.area.border, &back->border, sizeof(Border)); + } + else if (strcmp (key, "task_active_background_id") == 0) { + int id = atoi (value); + Area *back = g_slist_nth_data(list_back, id); + memcpy(&g_task.area_active.back, &back->back, sizeof(Color)); + memcpy(&g_task.area_active.border, &back->border, sizeof(Border)); + } + + /* Mouse actions */ + else if (strcmp (key, "mouse_middle") == 0) + get_action (value, &panel.mouse_middle); + else if (strcmp (key, "mouse_right") == 0) + get_action (value, &panel.mouse_right); + else if (strcmp (key, "mouse_scroll_up") == 0) + get_action (value, &panel.mouse_scroll_up); + else if (strcmp (key, "mouse_scroll_down") == 0) + get_action (value, &panel.mouse_scroll_down); + + + /* Read old config for backward compatibility */ + else if (strcmp (key, "font") == 0) { + panel.old_config_file = 1; + if (g_task.font_desc) pango_font_description_free(g_task.font_desc); + g_task.font_desc = pango_font_description_from_string (value); + if (panel.old_task_font) free(panel.old_task_font); + panel.old_task_font = strdup (value); + } + else if (strcmp (key, "font_color") == 0) + get_color (value, g_task.font.color); + else if (strcmp (key, "font_alpha") == 0) + g_task.font.alpha = (atoi (value) / 100.0); + else if (strcmp (key, "font_active_color") == 0) + get_color (value, g_task.font_active.color); + else if (strcmp (key, "font_active_alpha") == 0) + g_task.font_active.alpha = (atoi (value) / 100.0); + else if (strcmp (key, "panel_show_all_desktop") == 0) { + if (atoi (value) == 0) panel.mode = SINGLE_DESKTOP; + else panel.mode = MULTI_DESKTOP; + } + else if (strcmp (key, "panel_width") == 0) + panel.area.width = atoi (value); + else if (strcmp (key, "panel_height") == 0) + panel.area.height = atoi (value); + else if (strcmp (key, "panel_background") == 0) + panel.old_panel_background = atoi (value); + else if (strcmp (key, "panel_background_alpha") == 0) + panel.area.back.alpha = (atoi (value) / 100.0); + else if (strcmp (key, "panel_border_alpha") == 0) + panel.area.border.alpha = (atoi (value) / 100.0); + else if (strcmp (key, "task_icon") == 0) + panel.old_task_icon = atoi (value); + else if (strcmp (key, "task_background") == 0) + panel.old_task_background = atoi (value); + else if (strcmp (key, "task_background_alpha") == 0) + g_task.area.back.alpha = (atoi (value) / 100.0); + else if (strcmp (key, "task_active_background_alpha") == 0) + g_task.area_active.back.alpha = (atoi (value) / 100.0); + else if (strcmp (key, "task_border_alpha") == 0) + g_task.area.border.alpha = (atoi (value) / 100.0); + else if (strcmp (key, "task_active_border_alpha") == 0) + g_task.area_active.border.alpha = (atoi (value) / 100.0); + // disabled parameters + else if (strcmp (key, "task_active_border_width") == 0) ; + else if (strcmp (key, "task_active_rounded") == 0) ; + + else + fprintf(stderr, "Invalid option: \"%s\", correct your config file\n", key); + + if (value1) free (value1); + if (value2) free (value2); +} + + +int parse_line (const char *line) +{ + char *a, *b, *key, *value; + + /* Skip useless lines */ + if ((line[0] == '#') || (line[0] == '\n')) return 0; + if (!(a = strchr (line, '='))) return 0; + + /* overwrite '=' with '\0' */ + a[0] = '\0'; + key = strdup (line); + a++; + + /* overwrite '\n' with '\0' if '\n' present */ + if ((b = strchr (a, '\n'))) b[0] = '\0'; + + value = strdup (a); + + g_strstrip(key); + g_strstrip(value); + + add_entry (key, value); + + free (key); + free (value); + return 1; +} + + +void config_taskbar() +{ + int i, j; + + if (g_task.area.border.rounded > g_task.area.height/2) { + g_task.area.border.rounded = g_task.area.height/2; + g_task.area_active.border.rounded = g_task.area.border.rounded; + } + + for (i=0 ; i < 15 ; i++) { + server.nb_desktop = server_get_number_of_desktop (); + if (server.nb_desktop > 0) break; + sleep(1); + } + if (server.nb_desktop == 0) { + server.nb_desktop = 1; + fprintf(stderr, "tint2 warning : cannot found number of desktop.\n"); + } + + cleanup_taskbar(); + + panel.nb_desktop = server.nb_desktop; + if (panel.mode == MULTI_MONITOR) panel.nb_monitor = server.nb_monitor; + else panel.nb_monitor = 1; + + // TODO: mémoriser le pointeur sur la première + // malgré l'apparant désordre, les taskbars sont ordonnées + Taskbar *new_tskbar; + for (i=0 ; i < panel.nb_desktop ; i++) { + for (j=0 ; j < panel.nb_monitor ; j++) { + new_tskbar = calloc(1, sizeof(Taskbar)); + memcpy(&new_tskbar->area, &g_taskbar, sizeof(Area)); + new_tskbar->desktop = i; + new_tskbar->monitor = j; + + panel.area.list = g_slist_append(panel.area.list, new_tskbar); + } + } + /* + comment faire pour parcourir les barres de taches ? on ne peut pas se baser sur l'ordre des éléments !! + a t'on besoin de parcourir les barres de taches ?? OUI !! bof ?? + => resize_taskbar() dans panel.c => + => task_refresh_tasklist () dans taskbar.c + => Task *task_get_task (Window win) dans taskbar.c + => event_button_press (int x, int y) dans tint.c => area->event_button_press() est conseillé !! + cela enlève aussi l'organisation des barres de taches en tableau à 2 dimensions + il est possible de mémoriser un pointeur sur la première barre de taches +*/ + + //printf("tasbar (desktop x monitor) : (%d x %d)\n", panel.nb_desktop, panel.nb_monitor); + resize_taskbar(); + task_refresh_tasklist (); + panel.refresh = 1; +} + + +void config_finish () +{ + int height_ink, height; + + if (panel.old_config_file) save_config(); + + // get monitor's configuration + get_monitors(); + + if (panel.monitor > (server.nb_monitor-1)) { + panel.sleep_mode = 1; + printf("tint2 sleep and wait monitor %d.\n", panel.monitor+1); + } + else { + panel.sleep_mode = 0; + //printf("tint2 wake up on monitor %d\n", panel.monitor+1); + if (!server.monitor[panel.monitor].width || !server.monitor[panel.monitor].height) + fprintf(stderr, "tint2 error : invalid monitor size.\n"); + } + + if (!panel.area.width) panel.area.width = server.monitor[panel.monitor].width; + + // taskbar + g_taskbar.posy = panel.area.border.width + panel.area.paddingy; + g_taskbar.height = panel.area.height - (2 * g_taskbar.posy); + g_taskbar.redraw = 1; + + // task + g_task.area.posy = g_taskbar.posy + g_taskbar.border.width + g_taskbar.paddingy; + g_task.area_active.posy = g_task.area.posy; + g_task.area.height = panel.area.height - (2 * g_task.area.posy); + g_task.area_active.height = g_task.area.height; + g_task.area.redraw = 1; + + if (!g_task.maximum_width) + g_task.maximum_width = server.monitor[panel.monitor].width; + + if (panel.area.border.rounded > panel.area.height/2) + panel.area.border.rounded = panel.area.height/2; + + // clock + panel.clock.area.posy = panel.area.border.width + panel.area.paddingy; + panel.clock.area.height = panel.area.height - (2 * panel.clock.area.posy); + panel.clock.area.redraw = 1; + init_clock(&panel.clock, panel.area.height); + + // compute vertical position : text and icon + get_text_size(g_task.font_desc, &height_ink, &height, panel.area.height, "TAjpg", 5); + g_task.text_posy = (g_task.area.height - height) / 2.0; + + // add task_icon_size + g_task.text_posx = g_task.area.paddingx + g_task.area.border.width; + if (g_task.icon) { + g_task.icon_size1 = g_task.area.height - (2 * g_task.area.paddingy); + g_task.text_posx += g_task.icon_size1; + g_task.icon_posy = (g_task.area.height - g_task.icon_size1) / 2; + } + + config_taskbar(); + + // cleanup background list + GSList *l0; + for (l0 = list_back; l0 ; l0 = l0->next) { + free(l0->data); + } + g_slist_free(list_back); +} + + +int config_read () +{ + const gchar * const * system_dirs; + char *path1, *path2, *dir; + gint i; + + // check tint2rc file according to XDG specification + path1 = g_build_filename (g_get_user_config_dir(), "tint2", "tint2rc", NULL); + if (!g_file_test (path1, G_FILE_TEST_EXISTS)) { + + path2 = 0; + system_dirs = g_get_system_config_dirs(); + for (i = 0; system_dirs[i]; i++) { + path2 = g_build_filename(system_dirs[i], "tint2", "tint2rc", NULL); + + if (g_file_test(path2, G_FILE_TEST_EXISTS)) break; + g_free (path2); + path2 = 0; + } + + if (path2) { + // copy file in user directory (path1) + dir = g_build_filename (g_get_user_config_dir(), "tint2", NULL); + if (!g_file_test (dir, G_FILE_TEST_IS_DIR)) g_mkdir(dir, 0777); + g_free(dir); + + copy_file(path2, path1); + g_free(path2); + } + } + + i = config_read_file (path1); + g_free(path1); + return i; +} + + +int config_read_file (const char *path) +{ + FILE *fp; + char line[80]; + + if ((fp = fopen(path, "r")) == NULL) return 0; + + while (fgets(line, sizeof(line), fp) != NULL) + parse_line (line); + + fclose (fp); + return 1; +} + + +void save_config () +{ + fprintf(stderr, "tint2 warning : convert user's config file\n"); + panel.area.paddingx = panel.area.paddingy = panel.marginx; + panel.marginx = panel.marginy = 0; + + if (panel.old_task_icon == 0) g_task.icon_size1 = 0; + if (panel.old_panel_background == 0) panel.area.back.alpha = 0; + if (panel.old_task_background == 0) { + g_task.area.back.alpha = 0; + g_task.area_active.back.alpha = 0; + } + g_task.area.border.rounded = g_task.area.border.rounded / 2; + g_task.area_active.border.rounded = g_task.area.border.rounded; + panel.area.border.rounded = panel.area.border.rounded / 2; + + char *path; + FILE *fp; + + path = g_build_filename (g_get_user_config_dir(), "tint2", "tint2rc", NULL); + fp = fopen(path, "w"); + g_free(path); + if (fp == NULL) return; + + fputs("#---------------------------------------------\n", fp); + fputs("# TINT CONFIG FILE\n", fp); + fputs("#---------------------------------------------\n\n", fp); + fputs("#---------------------------------------------\n", fp); + fputs("# PANEL\n", fp); + fputs("#---------------------------------------------\n", fp); + if (panel.mode == SINGLE_DESKTOP) fputs("panel_mode = single_desktop\n", fp); + else fputs("panel_mode = multi_desktop\n", fp); + fputs("panel_monitor = 1\n", fp); + if (panel.position & BOTTOM) fputs("panel_position = bottom", fp); + else fputs("panel_position = top", fp); + if (panel.position & LEFT) fputs(" left\n", fp); + else if (panel.position & RIGHT) fputs(" right\n", fp); + else fputs(" center\n", fp); + fprintf(fp, "panel_size = %d %d\n", panel.area.width, panel.area.height); + fprintf(fp, "panel_margin = %d %d\n", panel.marginx, panel.marginy); + fprintf(fp, "panel_padding = %d %d\n", panel.area.paddingx, panel.area.paddingy); + fprintf(fp, "font_shadow = %d\n", g_task.font_shadow); + + fputs("\n#---------------------------------------------\n", fp); + fputs("# PANEL BACKGROUND AND BORDER\n", fp); + fputs("#---------------------------------------------\n", fp); + fprintf(fp, "panel_rounded = %d\n", panel.area.border.rounded); + fprintf(fp, "panel_border_width = %d\n", panel.area.border.width); + fprintf(fp, "panel_background_color = #%02x%02x%02x %d\n", (int)(panel.area.back.color[0]*255), (int)(panel.area.back.color[1]*255), (int)(panel.area.back.color[2]*255), (int)(panel.area.back.alpha*100)); + fprintf(fp, "panel_border_color = #%02x%02x%02x %d\n", (int)(panel.area.border.color[0]*255), (int)(panel.area.border.color[1]*255), (int)(panel.area.border.color[2]*255), (int)(panel.area.border.alpha*100)); + + fputs("\n#---------------------------------------------\n", fp); + fputs("# TASKS\n", fp); + fputs("#---------------------------------------------\n", fp); + fprintf(fp, "task_centered = %d\n", g_task.centered); + fprintf(fp, "task_width = %d\n", g_task.maximum_width); + fprintf(fp, "task_padding = %d\n", g_task.area.paddingx); + fprintf(fp, "task_icon = %d\n", g_task.icon); + fprintf(fp, "task_font = %s\n", panel.old_task_font); + fprintf(fp, "task_font_color = #%02x%02x%02x %d\n", (int)(g_task.font.color[0]*255), (int)(g_task.font.color[1]*255), (int)(g_task.font.color[2]*255), (int)(g_task.font.alpha*100)); + fprintf(fp, "task_active_font_color = #%02x%02x%02x %d\n", (int)(g_task.font_active.color[0]*255), (int)(g_task.font_active.color[1]*255), (int)(g_task.font_active.color[2]*255), (int)(g_task.font_active.alpha*100)); + + fputs("\n#---------------------------------------------\n", fp); + fputs("# TASK BACKGROUND AND BORDER\n", fp); + fputs("#---------------------------------------------\n", fp); + fprintf(fp, "task_rounded = %d\n", g_task.area.border.rounded); + fprintf(fp, "task_background_color = #%02x%02x%02x %d\n", (int)(g_task.area.back.color[0]*255), (int)(g_task.area.back.color[1]*255), (int)(g_task.area.back.color[2]*255), (int)(g_task.area.back.alpha*100)); + fprintf(fp, "task_active_background_color = #%02x%02x%02x %d\n", (int)(g_task.area_active.back.color[0]*255), (int)(g_task.area_active.back.color[1]*255), (int)(g_task.area_active.back.color[2]*255), (int)(g_task.area_active.back.alpha*100)); + fprintf(fp, "task_border_width = %d\n", g_task.area.border.width); + fprintf(fp, "task_border_color = #%02x%02x%02x %d\n", (int)(g_task.area.border.color[0]*255), (int)(g_task.area.border.color[1]*255), (int)(g_task.area.border.color[2]*255), (int)(g_task.area.border.alpha*100)); + fprintf(fp, "task_active_border_color = #%02x%02x%02x %d\n", (int)(g_task.area_active.border.color[0]*255), (int)(g_task.area_active.border.color[1]*255), (int)(g_task.area_active.border.color[2]*255), (int)(g_task.area_active.border.alpha*100)); + + fputs("\n#---------------------------------------------\n", fp); + fputs("# CLOCK\n", fp); + fputs("#---------------------------------------------\n", fp); + fputs("#time1_format = %H:%M\n", fp); + fputs("time1_font = sans bold 8\n", fp); + fputs("#time2_format = %A %d %B\n", fp); + fputs("time2_font = sans 6\n", fp); + fputs("clock_font_color = #ffffff 75\n", fp); + + fputs("\n#---------------------------------------------\n", fp); + fputs("# MOUSE ACTION ON TASK\n", fp); + fputs("#---------------------------------------------\n", fp); + if (panel.mouse_middle == NONE) fputs("mouse_middle = none\n", fp); + else if (panel.mouse_middle == CLOSE) fputs("mouse_middle = close\n", fp); + else if (panel.mouse_middle == TOGGLE) fputs("mouse_middle = toggle\n", fp); + else if (panel.mouse_middle == ICONIFY) fputs("mouse_middle = iconify\n", fp); + else if (panel.mouse_middle == SHADE) fputs("mouse_middle = shade\n", fp); + else fputs("mouse_middle = toggle_iconify\n", fp); + + if (panel.mouse_right == NONE) fputs("mouse_right = none\n", fp); + else if (panel.mouse_right == CLOSE) fputs("mouse_right = close\n", fp); + else if (panel.mouse_right == TOGGLE) fputs("mouse_right = toggle\n", fp); + else if (panel.mouse_right == ICONIFY) fputs("mouse_right = iconify\n", fp); + else if (panel.mouse_right == SHADE) fputs("mouse_right = shade\n", fp); + else fputs("mouse_right = toggle_iconify\n", fp); + + if (panel.mouse_scroll_up == NONE) fputs("mouse_scroll_up = none\n", fp); + else if (panel.mouse_scroll_up == CLOSE) fputs("mouse_scroll_up = close\n", fp); + else if (panel.mouse_scroll_up == TOGGLE) fputs("mouse_scroll_up = toggle\n", fp); + else if (panel.mouse_scroll_up == ICONIFY) fputs("mouse_scroll_up = iconify\n", fp); + else if (panel.mouse_scroll_up == SHADE) fputs("mouse_scroll_up = shade\n", fp); + else fputs("mouse_scroll_up = toggle_iconify\n", fp); + + if (panel.mouse_scroll_down == NONE) fputs("mouse_scroll_down = none\n", fp); + else if (panel.mouse_scroll_down == CLOSE) fputs("mouse_scroll_down = close\n", fp); + else if (panel.mouse_scroll_down == TOGGLE) fputs("mouse_scroll_down = toggle\n", fp); + else if (panel.mouse_scroll_down == ICONIFY) fputs("mouse_scroll_down = iconify\n", fp); + else if (panel.mouse_scroll_down == SHADE) fputs("mouse_scroll_down = shade\n", fp); + else fputs("mouse_scroll_down = toggle_iconify\n", fp); + + fputs("\n\n", fp); + fclose (fp); + + panel.old_config_file = 0; +} + diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..1fcd1ed --- /dev/null +++ b/src/config.h @@ -0,0 +1,24 @@ +/************************************************************************** +* config : +* - parse config file in Panel struct. +* +* Check COPYING file for Copyright +* +**************************************************************************/ + +#ifndef CONFIG_H +#define CONFIG_H + + +GSList *list_back; + + +int config_read_file (const char *path); +int config_read (); +void config_taskbar(); +void config_finish (); +void cleanup_taskbar(); +void cleanup (); +void save_config (); + +#endif diff --git a/src/panel.c b/src/panel.c new file mode 100644 index 0000000..2e1c228 --- /dev/null +++ b/src/panel.c @@ -0,0 +1,249 @@ +/************************************************************************** +* +* Copyright (C) 2008 Pål Staurland (staura@gmail.com) +* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "server.h" +#include "window.h" +#include "task.h" +#include "panel.h" + + +void visual_refresh () +{ + server_refresh_root_pixmap (); + + draw (&panel.area); + refresh (&panel.area); + +/* +pour version 0.7 +gestion du systray + positionnement et taille fixe du systray (objet systray) + détection des notifications (détection des icones, ajout a la liste) + ajouter la transparence des icones + gérer le redimentionnement des éléments + => voir si lon peut faire abstraction sur le positionnement des objets ? + sachant que certains objets (task, taskbar) on une taille définit par l'extérieur + et d'autres objets (clock, systray) on une taille définit par l'intérieur + +gestion du layout + voir le positionnement des taskbar, task et systray + définir panel_layout dans la configuration + comment gérer le multi panel avec des layouts différents + +vérifier le niveau d'abstraction du code + utiliser la fonction draw(obj) récurrente sur Taskbar, Task, Systray, Clock + est ce compatible avec l'affichage de la tache active et les changement de taille -> redessine le panel + +correction de bugs : + memory, segfault + background + remettre en place single_desktop avec nouveau layout + remettre en place multi_monitor avec nouveau layout + vérifier le changement de configuration + +pour version 0.8 +gestion du thème + voir la gestion du dégradé sur le bord et le fond (inkscape) + faut-il trois coordonnées de padding x, y, x inter-objects + +gestion du zoom + définir le zoom du panel + +*/ + + if (panel.clock.time1_format) { + if (panel.clock.area.redraw) + panel.refresh = 1; + if (draw (&panel.clock.area)) { + panel.clock.area.redraw = 1; + draw (&panel.clock.area); + resize_clock(); + resize_taskbar(); + redraw(&panel.area); + } + refresh (&panel.clock.area); + } + + // TODO: ne pas afficher les taskbar invisibles + //if (panel.mode != MULTI_DESKTOP && desktop != server.desktop) continue; + Task *tsk; + Taskbar *tskbar; + GSList *l0; + for (l0 = panel.area.list; l0 ; l0 = l0->next) { + tskbar = l0->data; + draw (&tskbar->area); + refresh (&tskbar->area); + + GSList *l1; + for (l1 = tskbar->area.list; l1 ; l1 = l1->next) { + tsk = l1->data; + draw(&tsk->area); + + if (tsk == panel.task_active) refresh (&tsk->area_active); + else refresh (&tsk->area); + } + } + + XCopyArea (server.dsp, server.pmap, window.main_win, server.gc, 0, 0, panel.area.width, panel.area.height, 0, 0); + XFlush(server.dsp); + panel.refresh = 0; +} + + +void set_panel_properties (Window win) +{ + XStoreName (server.dsp, win, "tint2"); + + // TODO: check if the name is really needed for a panel/taskbar ? + gsize len; + gchar *name = g_locale_to_utf8("tint2", -1, NULL, &len, NULL); + if (name != NULL) { + XChangeProperty(server.dsp, win, server.atom._NET_WM_NAME, server.atom.UTF8_STRING, 8, PropModeReplace, (unsigned char *) name, (int) len); + g_free(name); + } + + // Dock + long val = server.atom._NET_WM_WINDOW_TYPE_DOCK; + XChangeProperty (server.dsp, win, server.atom._NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *) &val, 1); + + // Reserved space + long struts [12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + if (panel.position & TOP) { + struts[2] = panel.area.height + panel.marginy; + struts[8] = server.posx; + struts[9] = server.posx + panel.area.width; + } + else { + struts[3] = panel.area.height + panel.marginy; + struts[10] = server.posx; + struts[11] = server.posx + panel.area.width; + } + XChangeProperty (server.dsp, win, server.atom._NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &struts, 12); + // Old specification + XChangeProperty (server.dsp, win, server.atom._NET_WM_STRUT, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &struts, 4); + + // Sticky and below other window + val = 0xFFFFFFFF; + XChangeProperty (server.dsp, win, server.atom._NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &val, 1); + Atom state[4]; + state[0] = server.atom._NET_WM_STATE_SKIP_PAGER; + state[1] = server.atom._NET_WM_STATE_SKIP_TASKBAR; + state[2] = server.atom._NET_WM_STATE_STICKY; + state[3] = server.atom._NET_WM_STATE_BELOW; + XChangeProperty (server.dsp, win, server.atom._NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char *) state, 4); + + // Fixed position + XSizeHints size_hints; + size_hints.flags = PPosition; + XChangeProperty (server.dsp, win, XA_WM_NORMAL_HINTS, XA_WM_SIZE_HINTS, 32, PropModeReplace, (unsigned char *) &size_hints, sizeof (XSizeHints) / 4); + + // Unfocusable + XWMHints wmhints; + wmhints.flags = InputHint; + wmhints.input = False; + XChangeProperty (server.dsp, win, XA_WM_HINTS, XA_WM_HINTS, 32, PropModeReplace, (unsigned char *) &wmhints, sizeof (XWMHints) / 4); +} + + +void window_draw_panel () +{ + Window win; + + /* panel position determined here */ + if (panel.position & LEFT) server.posx = server.monitor[panel.monitor].x + panel.marginx; + else { + if (panel.position & RIGHT) server.posx = server.monitor[panel.monitor].x + server.monitor[panel.monitor].width - panel.area.width - panel.marginx; + else server.posx = server.monitor[panel.monitor].x + ((server.monitor[panel.monitor].width - panel.area.width) / 2); + } + if (panel.position & TOP) server.posy = server.monitor[panel.monitor].y + panel.marginy; + else server.posy = server.monitor[panel.monitor].y + server.monitor[panel.monitor].height - panel.area.height - panel.marginy; + + /* Catch some events */ + XSetWindowAttributes att = { ParentRelative, 0L, 0, 0L, 0, 0, Always, 0L, 0L, False, ExposureMask|ButtonPressMask|ButtonReleaseMask, NoEventMask, False, 0, 0 }; + + /* XCreateWindow(display, parent, x, y, w, h, border, depth, class, visual, mask, attrib) */ + if (window.main_win) XDestroyWindow(server.dsp, window.main_win); + win = XCreateWindow (server.dsp, server.root_win, server.posx, server.posy, panel.area.width, panel.area.height, 0, server.depth, InputOutput, CopyFromParent, CWEventMask, &att); + + set_panel_properties (win); + window.main_win = win; + + // replaced : server.gc = DefaultGC (server.dsp, 0); + if (server.gc) XFree(server.gc); + XGCValues gcValues; + server.gc = XCreateGC(server.dsp, win, (unsigned long) 0, &gcValues); + + XMapWindow (server.dsp, win); + XFlush (server.dsp); +} + + +void resize_clock() +{ + panel.clock.area.posx = panel.area.width - panel.clock.area.width - panel.area.paddingx - panel.area.border.width; +} + + +// initialise taskbar posx and width +void resize_taskbar() +{ + int taskbar_width, modulo_width, taskbar_on_screen; + + if (panel.mode == MULTI_DESKTOP) taskbar_on_screen = panel.nb_desktop; + else taskbar_on_screen = panel.nb_monitor; + + taskbar_width = panel.area.width - (2 * panel.area.paddingx) - (2 * panel.area.border.width); + if (panel.clock.time1_format) + taskbar_width -= (panel.clock.area.width + panel.area.paddingx); + taskbar_width = (taskbar_width - ((taskbar_on_screen-1) * panel.area.paddingx)) / taskbar_on_screen; + + if (taskbar_on_screen > 1) + modulo_width = (taskbar_width - ((taskbar_on_screen-1) * panel.area.paddingx)) % taskbar_on_screen; + else + modulo_width = 0; + + int posx, modulo, i; + Taskbar *tskbar; + GSList *l0; + for (i = 0, l0 = panel.area.list; l0 ; i++, l0 = l0->next) { + if ((i % taskbar_on_screen) == 0) { + posx = panel.area.border.width + panel.area.paddingx; + modulo = modulo_width; + } + else posx += taskbar_width + panel.area.paddingx; + + tskbar = l0->data; + tskbar->area.posx = posx; + tskbar->area.width = taskbar_width; + if (modulo) { + tskbar->area.width++; + modulo--; + } + + resize_tasks(tskbar); + } +} + + diff --git a/src/panel.h b/src/panel.h new file mode 100644 index 0000000..1acd611 --- /dev/null +++ b/src/panel.h @@ -0,0 +1,85 @@ +/************************************************************************** +* panel : +* - draw panel and all objects according to panel_layout +* +* Check COPYING file for Copyright +* +**************************************************************************/ + +#ifndef PANEL_H +#define PANEL_H + +#include +#include + +#include "common.h" +#include "clock.h" +#include "task.h" +#include "taskbar.h" + + +//panel mode +enum { SINGLE_DESKTOP=0, MULTI_DESKTOP, MULTI_MONITOR }; + +//panel alignment +enum { LEFT=0x01, RIGHT=0x02, CENTER=0X04, TOP=0X08, BOTTOM=0x10 }; + + +typedef struct { + // -------------------------------------------------- + // always start with area + Area area; + + // -------------------------------------------------- + // backward compatibility + int old_config_file; + int old_task_icon; + int old_panel_background; + int old_task_background; + char *old_task_font; + + // -------------------------------------------------- + // panel + int signal_pending; + int sleep_mode; + int refresh; + int monitor; + int position; + int marginx, marginy; + + // -------------------------------------------------- + // taskbar point to the first taskbar in panel.area.list. number of tasbar == nb_desktop x nb_monitor. + //Taskbar *taskbar; + int mode; + int nb_desktop; + int nb_monitor; + Task *task_active; + Task *task_drag; + + // -------------------------------------------------- + // clock + Clock clock; + + // -------------------------------------------------- + // systray + + // -------------------------------------------------- + // mouse events + int mouse_middle; + int mouse_right; + int mouse_scroll_up; + int mouse_scroll_down; +} Panel; + + +Panel panel; + + +void visual_refresh (); +void set_panel_properties (Window win); +void window_draw_panel (); +void resize_clock(); +void resize_taskbar(); + + +#endif diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..cdfa969 --- /dev/null +++ b/src/server.c @@ -0,0 +1,216 @@ +/************************************************************************** +* +* Tint2 panel +* +* Copyright (C) 2007 Pål Staurland (staura@gmail.com) +* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include + +#include "server.h" +#include "panel.h" +#include "task.h" +#include "window.h" + +void server_catch_error (Display *d, XErrorEvent *ev){} + + +void server_init_atoms () +{ + server.atom._XROOTPMAP_ID = XInternAtom (server.dsp, "_XROOTPMAP_ID", False); + server.atom._NET_CURRENT_DESKTOP = XInternAtom (server.dsp, "_NET_CURRENT_DESKTOP", False); + server.atom._NET_NUMBER_OF_DESKTOPS = XInternAtom (server.dsp, "_NET_NUMBER_OF_DESKTOPS", False); + server.atom._NET_DESKTOP_GEOMETRY = XInternAtom (server.dsp, "_NET_DESKTOP_GEOMETRY", False); + server.atom._NET_DESKTOP_VIEWPORT = XInternAtom (server.dsp, "_NET_DESKTOP_VIEWPORT", False); + server.atom._NET_ACTIVE_WINDOW = XInternAtom (server.dsp, "_NET_ACTIVE_WINDOW", False); + server.atom._NET_WM_WINDOW_TYPE = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE", False); + server.atom._NET_WM_STATE_SKIP_PAGER = XInternAtom (server.dsp, "_NET_WM_STATE_SKIP_PAGER", False); + server.atom._NET_WM_STATE_SKIP_TASKBAR = XInternAtom (server.dsp, "_NET_WM_STATE_SKIP_TASKBAR", False); + server.atom._NET_WM_STATE_STICKY = XInternAtom (server.dsp, "_NET_WM_STATE_STICKY", False); + server.atom._NET_WM_WINDOW_TYPE_DOCK = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_DOCK", False); + server.atom._NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_DESKTOP", False); + server.atom._NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); + server.atom._NET_WM_WINDOW_TYPE_MENU = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_MENU", False); + server.atom._NET_WM_WINDOW_TYPE_SPLASH = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_SPLASH", False); + server.atom._NET_WM_WINDOW_TYPE_DIALOG = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_DIALOG", False); + server.atom._NET_WM_WINDOW_TYPE_NORMAL = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_NORMAL", False); + server.atom._NET_WM_DESKTOP = XInternAtom (server.dsp, "_NET_WM_DESKTOP", False); + server.atom.WM_STATE = XInternAtom (server.dsp, "WM_STATE", False); + server.atom._NET_WM_STATE = XInternAtom (server.dsp, "_NET_WM_STATE", False); + server.atom._NET_WM_STATE_SHADED = XInternAtom (server.dsp, "_NET_WM_STATE_SHADED", False); + server.atom._NET_WM_STATE_BELOW = XInternAtom (server.dsp, "_NET_WM_STATE_BELOW", False); + server.atom._NET_WM_STATE_MODAL = XInternAtom (server.dsp, "_NET_WM_STATE_MODAL", False); + server.atom._NET_CLIENT_LIST = XInternAtom (server.dsp, "_NET_CLIENT_LIST", False); + server.atom._NET_WM_VISIBLE_NAME = XInternAtom (server.dsp, "_NET_WM_VISIBLE_NAME", False); + server.atom._NET_WM_NAME = XInternAtom (server.dsp, "_NET_WM_NAME", False); + server.atom._NET_WM_STRUT = XInternAtom (server.dsp, "_NET_WM_STRUT", False); + server.atom._NET_WM_ICON = XInternAtom (server.dsp, "_NET_WM_ICON", False); + server.atom._NET_CLOSE_WINDOW = XInternAtom (server.dsp, "_NET_CLOSE_WINDOW", False); + server.atom.UTF8_STRING = XInternAtom (server.dsp, "UTF8_STRING", False); + server.atom._NET_SUPPORTING_WM_CHECK = XInternAtom (server.dsp, "_NET_SUPPORTING_WM_CHECK", False); + server.atom._NET_SUPPORTING_WM_CHECK = XInternAtom (server.dsp, "_NET_WM_NAME", False); + server.atom._WIN_LAYER = XInternAtom (server.dsp, "_WIN_LAYER", False); + server.atom._NET_WM_STRUT_PARTIAL = XInternAtom (server.dsp, "_NET_WM_STRUT_PARTIAL", False); + server.atom.WM_NAME = XInternAtom(server.dsp, "WM_NAME", False); +} + + +void send_event32 (Window win, Atom at, long data1, long data2) +{ + XEvent event; + + event.xclient.type = ClientMessage; + event.xclient.serial = 0; + event.xclient.send_event = True; + event.xclient.display = server.dsp; + event.xclient.window = win; + event.xclient.message_type = at; + + event.xclient.format = 32; + event.xclient.data.l[0] = data1; + event.xclient.data.l[1] = data2; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + XSendEvent(server.dsp, server.root_win, False, SubstructureRedirectMask|SubstructureNotifyMask, &event); +} + + +int get_property32 (Window win, Atom at, Atom type) +{ + Atom type_ret; + int format_ret = 0, data = 0; + unsigned long nitems_ret = 0; + unsigned long bafter_ret = 0; + unsigned char *prop_value = 0; + int result; + + if (!win) return 0; + + result = XGetWindowProperty(server.dsp, win, at, 0, 0x7fffffff, False, type, &type_ret, &format_ret, &nitems_ret, &bafter_ret, &prop_value); + + if (result == Success && prop_value) { + data = ((gulong*)prop_value)[0]; + XFree (prop_value); + } + return data; +} + + +void *server_get_property (Window win, Atom at, Atom type, int *num_results) +{ + Atom type_ret; + int format_ret = 0; + unsigned long nitems_ret = 0; + unsigned long bafter_ret = 0; + unsigned char *prop_value; + int result; + + if (!win) return 0; + + result = XGetWindowProperty(server.dsp, win, at, 0, 0x7fffffff, False, type, &type_ret, &format_ret, &nitems_ret, &bafter_ret, &prop_value); + + /* Send back resultcount */ + if (num_results) *num_results = nitems_ret; + + if (result == Success && prop_value) return prop_value; + else return 0; +} + + +Pixmap get_root_pixmap () +{ + // conky capture correctement le fond d'écran en xlib !! + Pixmap root_pixmap; + unsigned long *res; + + res = server_get_property (server.root_win, server.atom._XROOTPMAP_ID, XA_PIXMAP, 0); + if (res) { + root_pixmap = *((Drawable*) res); + XFree(res); + return root_pixmap; + } + else { + printf("get_root_pixmap incorrect\n"); + // try _XSETROOT_ID + } + + return 0; +} + + + + +Pixmap server_create_pixmap (int width, int height) +{ + return XCreatePixmap (server.dsp, server.root_win, width, height, server.depth); +} + + +void server_refresh_root_pixmap () +{ + if (!server.root_pmap) { + Pixmap wall = get_root_pixmap(); + + server.root_pmap = server_create_pixmap (panel.area.width, panel.area.height); + + XCopyArea (server.dsp, wall, server.root_pmap, server.gc, server.posx, server.posy, panel.area.width, panel.area.height, 0, 0); + + panel.area.redraw = 1; + } + + if (server.pmap) XFreePixmap (server.dsp, server.pmap); + server.pmap = server_create_pixmap (panel.area.width, panel.area.height); + + XCopyArea (server.dsp, server.root_pmap, server.pmap, server.gc, 0, 0, panel.area.width, panel.area.height, 0, 0); +} + + +void get_monitors() +{ + if (server.monitor) free(server.monitor); + server.nb_monitor = 0; + server.monitor = 0; + + if (XineramaIsActive(server.dsp)) { + XineramaScreenInfo *info = XineramaQueryScreens(server.dsp, &server.nb_monitor); + + if (info) { + int i; + + server.monitor = calloc(server.nb_monitor, sizeof(Monitor)); + for (i = 0; i < server.nb_monitor; i++) { + server.monitor[i].x = info[i].x_org; + server.monitor[i].y = info[i].y_org; + server.monitor[i].width = info[i].width; + server.monitor[i].height = info[i].height; + } + XFree(info); + } + } + + if (!server.nb_monitor) { + server.nb_monitor = 1; + server.monitor = calloc(server.nb_monitor, sizeof(Monitor)); + server.monitor[0].x = server.monitor[0].y = 0; + server.monitor[0].width = DisplayWidth (server.dsp, server.screen); + server.monitor[0].height = DisplayHeight (server.dsp, server.screen); + } +} + + diff --git a/src/server.h b/src/server.h new file mode 100644 index 0000000..6bb4060 --- /dev/null +++ b/src/server.h @@ -0,0 +1,102 @@ +/************************************************************************** +* server : +* - +* +* Check COPYING file for Copyright +* +**************************************************************************/ + +#ifndef SERVER_H +#define SERVER_H + +#include +#include +#include +#include + + +typedef struct Global_atom +{ + Atom _XROOTPMAP_ID; + Atom _NET_CURRENT_DESKTOP; + Atom _NET_NUMBER_OF_DESKTOPS; + Atom _NET_DESKTOP_GEOMETRY; + Atom _NET_DESKTOP_VIEWPORT; + Atom _NET_ACTIVE_WINDOW; + Atom _NET_WM_WINDOW_TYPE; + Atom _NET_WM_STATE_SKIP_PAGER; + Atom _NET_WM_STATE_SKIP_TASKBAR; + Atom _NET_WM_STATE_STICKY; + Atom _NET_WM_WINDOW_TYPE_DOCK; + Atom _NET_WM_WINDOW_TYPE_DESKTOP; + Atom _NET_WM_WINDOW_TYPE_TOOLBAR; + Atom _NET_WM_WINDOW_TYPE_MENU; + Atom _NET_WM_WINDOW_TYPE_SPLASH; + Atom _NET_WM_WINDOW_TYPE_DIALOG; + Atom _NET_WM_WINDOW_TYPE_NORMAL; + Atom _NET_WM_DESKTOP; + Atom WM_STATE; + Atom _NET_WM_STATE; + Atom _NET_WM_STATE_SHADED; + Atom _NET_WM_STATE_BELOW; + Atom _NET_WM_STATE_MODAL; + Atom _NET_CLIENT_LIST; + Atom _NET_WM_NAME; + Atom _NET_WM_VISIBLE_NAME; + Atom _NET_WM_STRUT; + Atom _NET_WM_ICON; + Atom _NET_CLOSE_WINDOW; + Atom UTF8_STRING; + Atom _NET_SUPPORTING_WM_CHECK; + Atom _WIN_LAYER; + Atom _NET_WM_STRUT_PARTIAL; + Atom WM_NAME; +} Global_atom; + + + +typedef struct Monitor +{ + int x; + int y; + int width; + int height; +} Monitor; + + +typedef struct +{ + Display *dsp; + Window root_win; + int desktop; + int screen; + int depth; + int nb_desktop; + Monitor *monitor; + int nb_monitor; + int got_root_win; + Visual *visual; + int posx, posy; + Pixmap pmap; + Pixmap root_pmap; + GC gc; + Global_atom atom; +} Server_global; + + +Server_global server; + + +void send_event32 (Window win, Atom at, long data1, long data2); +int get_property32 (Window win, Atom at, Atom type); +void *server_get_property (Window win, Atom at, Atom type, int *num_results); +Atom server_get_atom (char *atom_name); +void server_refresh_root_pixmap (); +void server_refresh_main_pixmap (); +void server_catch_error (Display *d, XErrorEvent *ev); +void server_init_atoms (); +Pixmap server_create_pixmap (int width, int height); +void get_monitors(); + + +#endif diff --git a/src/systray/Makefile b/src/systray/Makefile new file mode 100644 index 0000000..03a2270 --- /dev/null +++ b/src/systray/Makefile @@ -0,0 +1,71 @@ +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# +# # +# Change these values to customize your installation and build process # +# # +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# + +# Change this PREFIX to where you want docker to be installed +PREFIX=/usr/local +# Change this XLIBPATH to point to your X11 development package's installation +XLIBPATH=/usr/X11R6/lib + +# Sets some flags for stricter compiling +CFLAGS=-pedantic -Wall -W -O + +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# +# # +# Leave the rest of the Makefile alone if you want it to build! # +# # +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# + +PACKAGE=docker +VERSION=1.5 + +target=docker +sources=docker.c kde.c icons.c xproperty.c net.c +headers=docker.h kde.h icons.h xproperty.h net.h version.h +extra=README COPYING version.h.in + +all: $(target) $(sources) $(headers) + @echo Build Successful + +$(target): $(sources:.c=.o) + $(CC) $(CFLAGS) -L$(XLIBPATH) -lX11 \ + `pkg-config --libs glib-2.0` $^ -o $@ + +%.o: %.c + $(CC) -c $(CFLAGS) `pkg-config --cflags glib-2.0` $< + +version.h: version.h.in Makefile + sed -e "s/@VERSION@/$(VERSION)/" version.h.in > $@ + +install: all + install $(target) $(PREFIX)/bin/$(target) + +uninstall: + rm -f $(PREFIX)/$(target) + +clean: + rm -rf .dist + rm -f core *.o .\#* *\~ $(target) + +distclean: clean + rm -f version.h + rm -f $(PACKAGE)-*.tar.gz + +dist: Makefile $(sources) $(headers) $(extra) + mkdir -p .dist/$(PACKAGE)-$(VERSION) && \ + cp $^ .dist/$(PACKAGE)-$(VERSION) && \ + tar -c -z -C .dist -f \ + $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION) && \ + rm -rf .dist + +love: $(sources) + touch $^ + +# local dependancies +docker.o: docker.c version.h kde.h icons.h docker.h net.h +icons.o: icons.c icons.h docker.h +kde.o: kde.c kde.h docker.h xproperty.h +net.o: net.c net.h docker.h icons.h +xproperty.o: xproperty.c xproperty.h docker.h diff --git a/src/systray/README b/src/systray/README new file mode 100644 index 0000000..c851765 --- /dev/null +++ b/src/systray/README @@ -0,0 +1,75 @@ +Docker - Docking System Tray + +Copyright (C) 2003 Ben Jansens + + +What is Docker? + +Docker is a docking application (WindowMaker dock app) which acts as a system +tray for KDE3 and GNOME2. It can be used to replace the panel in either +environment, allowing you to have a system tray without running the KDE/GNOME +panel. + + +What window managers can I use Docker with? + +I wrote and designed Docker to work with Openbox 2, but it should work fine in +any window manager that supports WindowMaker dock apps. + + +Why don't my KDE3 system tray icons show up? + +Docker requires a KDE3 compliant window manager to handle KDE3 system tray +icons, and since it is a docking application, the window manager needs to also +support WindowMaker Dock Apps. The only window manager that meets these +requirements to my knowledge is: + - Openbox 2 (http://icculus.org/openbox) +If you know of any other window managers that support the KDE3 hints for the +system tray and docking apps (i.e. that docker works in), please let me know so +I can add them to this list, and test docker out in them! + + +Why don't my GNOME2 system tray icons show up? + +I don't know! Email me and let me know what application isn't working. (Don't +you dare email me about a GNOME1 application! :) + + +Who wrote Docker? + +Me, of course. That is, Ben Jansens. I can be reached at . I am +the founder and currently the project head of sorts for the Openbox project. + + +=============================== +|| INSTALLATION INSTRUCTIONS || +=============================== + +To install this application, simply do the following: + +% make +(as root) +# make install + +You can change a couple of things in the Makefile if you want to: +PREFIX defines where the program will be installed to. +XLIBPATH defines where your libX11.so is located. If it is not on the standard + /usr/X11R6/lib path, then you will have to change this. + +================== +|| LICENSE INFO || +================== + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/src/systray/docker.c b/src/systray/docker.c new file mode 100644 index 0000000..091c62e --- /dev/null +++ b/src/systray/docker.c @@ -0,0 +1,346 @@ +#include "version.h" +#include "kde.h" +#include "icons.h" +#include "docker.h" +#include "net.h" + +#include +#include +#include +#include +#include +#include + +int argc; +char **argv; + +Window win = None, hint_win = None, root = None; +gboolean wmaker = FALSE; /* WindowMakerMode!!! wheeee */ +Display *display = NULL; +GSList *icons = NULL; +int width = 0, height = 0; +int border = 1; /* blank area around icons. must be > 0 */ +gboolean horizontal = TRUE; /* layout direction */ +int icon_size = 24; /* width and height of systray icons */ + +//static char *display_string = NULL; +/* excluding the border. sum of all child apps */ +static gboolean exit_app = FALSE; + +/* +void parse_cmd_line() +{ + int i; + gboolean help = FALSE; + + for (i = 1; i < argc; i++) { + if (0 == strcasecmp(argv[i], "-display")) { + ++i; + if (i < argc) { + display_string = argv[i]; + } else { + g_printerr("-display requires a parameter\n"); + help = TRUE; + } + } else if (0 == strcasecmp(argv[i], "-wmaker")) { + wmaker = TRUE; + } else if (0 == strcasecmp(argv[i], "-vertical")) { + horizontal = FALSE; + } else if (0 == strcasecmp(argv[i], "-border")) { + ++i; + + if (i < argc) { + int b = atoi(argv[i]); + if (b > 0) { + border = b; + } else { + g_printerr("-border must be a value greater than 0\n"); + help = TRUE; + } + } else { + g_printerr("-border requires a parameter\n"); + help = TRUE; + } + } else if (0 == strcasecmp(argv[i], "-iconsize")) { + ++i; + if (i < argc) { + int s = atoi(argv[i]); + if (s > 0) { + icon_size = s; + } else { + g_printerr("-iconsize must be a value greater than 0\n"); + help = TRUE; + } + } else { + g_printerr("-iconsize requires a parameter\n"); + help = TRUE; + } + } else { + if (argv[i][0] == '-') + help = TRUE; + } + + + if (help) { + + g_print("%s - version %s\n", argv[0], VERSION); + g_print("Copyright 2003, Ben Jansens \n\n"); + g_print("Usage: %s [OPTIONS]\n\n", argv[0]); + g_print("Options:\n"); + g_print(" -help Show this help.\n"); + g_print(" -display DISLPAY The X display to connect to.\n"); + g_print(" -border The width of the border to put around the\n" + " system tray icons. Defaults to 1.\n"); + g_print(" -vertical Line up the icons vertically. Defaults to\n" + " horizontally.\n"); + g_print(" -wmaker WindowMaker mode. This makes docker a\n" + " fixed size (64x64) to appear nicely in\n" + " in WindowMaker.\n" + " Note: In this mode, you have a fixed\n" + " number of icons that docker can hold.\n"); + g_print(" -iconsize SIZE The size (width and height) to display\n" + " icons as in the system tray. Defaults to\n" + " 24.\n"); + exit(1); + } + } +} +*/ + +void create_hint_win() +{ + XWMHints hints; + XClassHint classhints; + + hint_win = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, 0, 0); + assert(hint_win); + + hints.flags = StateHint | WindowGroupHint | IconWindowHint; + hints.initial_state = WithdrawnState; + hints.window_group = hint_win; + hints.icon_window = win; + + classhints.res_name = "docker"; + classhints.res_class = "Docker"; + + XSetWMProperties(display, hint_win, NULL, NULL, argv, argc, + NULL, &hints, &classhints); + + XMapWindow(display, hint_win); +} + + +void create_main_window() +{ + XWMHints hints; + XTextProperty text; + char *name = "Docker"; + + /* the border must be > 0 if not in wmaker mode */ + assert(wmaker || border > 0); + + if (!wmaker) + win = XCreateSimpleWindow(display, root, 0, 0, + border * 2, border * 2, 0, 0, 0); + else + win = XCreateSimpleWindow(display, root, 0, 0, + 64, 64, 0, 0, 0); + + assert(win); + + XStringListToTextProperty(&name, 1, &text); + XSetWMName(display, win, &text); + + hints.flags = StateHint; + hints.initial_state = WithdrawnState; + XSetWMHints(display, win, &hints); + + create_hint_win(); + + XSync(display, False); + XSetWindowBackgroundPixmap(display, win, ParentRelative); + XClearWindow(display, win); +} + + +void reposition_icons() +{ + int x = border + ((width % icon_size) / 2), + y = border + ((height % icon_size) / 2); + GSList *it; + + for (it = icons; it != NULL; it = g_slist_next(it)) { + TrayWindow *traywin = it->data; + traywin->x = x; + traywin->y = y; + XMoveWindow(display, traywin->id, x, y); + XSync(display, False); + if (wmaker) { + x += icon_size; + if (x + icon_size > width) { + x = border; + y += icon_size; + } + } else if (horizontal) + x += icon_size; + else + y += icon_size; + } +} + + +void fix_geometry() +{ + GSList *it; + + /* in wmaker mode we're a fixed size */ + if (wmaker) return; + + /* find the proper width and height */ + width = horizontal ? 0 : icon_size; + height = horizontal ? icon_size : 0; + for (it = icons; it != NULL; it = g_slist_next(it)) { + if (horizontal) + width += icon_size; + else + height += icon_size; + } + + XResizeWindow(display, win, width + border * 2, height + border * 2); +} + + +void event_loop() +{ + XEvent e; + Window cover; + GSList *it; + + while (!exit_app) { + while (XPending(display)) { + XNextEvent(display, &e); + + switch (e.type) + { + case PropertyNotify: + /* systray window list has changed? */ + if (e.xproperty.atom == kde_systray_prop) { + XSelectInput(display, win, NoEventMask); + kde_update_icons(); + XSelectInput(display, win, StructureNotifyMask); + + while (XCheckTypedEvent(display, PropertyNotify, &e)); + } + + break; + + case ConfigureNotify: + if (e.xany.window != win) { + /* find the icon it pertains to and beat it into submission */ + GSList *it; + + for (it = icons; it != NULL; it = g_slist_next(it)) { + TrayWindow *traywin = it->data; + if (traywin->id == e.xany.window) { + XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y, + icon_size, icon_size); + break; + } + } + break; + } + + /* briefly cover the entire containing window, which causes it and + all of the icons to refresh their windows. finally, they update + themselves when the background of the main window's parent changes. + */ + cover = XCreateSimpleWindow(display, win, 0, 0, + border * 2 + width, border * 2 + height, + 0, 0, 0); + XMapWindow(display, cover); + XDestroyWindow(display, cover); + + break; + + case ReparentNotify: + if (e.xany.window == win) /* reparented to us */ + break; + case UnmapNotify: + case DestroyNotify: + for (it = icons; it; it = g_slist_next(it)) { + if (((TrayWindow*)it->data)->id == e.xany.window) { + icon_remove(it); + break; + } + } + break; + + case ClientMessage: + if (e.xclient.message_type == net_opcode_atom && + e.xclient.format == 32 && + e.xclient.window == net_sel_win) + net_message(&e.xclient); + + default: + break; + } + } + usleep(500000); + } + + /* remove/unparent all the icons */ + while (icons) { + /* do the remove here explicitly, cuz the event handler isn't going to + happen anymore. */ + icon_remove(icons); + } +} + +/* +int main(int c, char **v) +{ + struct sigaction act; + + argc = c; argv = v; + + act.sa_handler = signal_handler; + act.sa_flags = 0; + sigaction(SIGSEGV, &act, NULL); + sigaction(SIGPIPE, &act, NULL); + sigaction(SIGFPE, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + + parse_cmd_line(argc, argv); + + display = XOpenDisplay(display_string); + if (!display) { + g_printerr("Unable to open Display %s. Exiting.\n", + DisplayString(display_string)); + } + + root = RootWindow(display, DefaultScreen(display)); + assert(root); + + if (wmaker) + width = height = 64 - border * 2; + + create_main_window(); + + // set up to find KDE systray icons, and get any that already exist + kde_init(); + + net_init(); + + // we want to get ConfigureNotify events, and assume our parent's background + // has changed when we do, so we need to refresh ourself to match + XSelectInput(display, win, StructureNotifyMask); + + event_loop(); + + XCloseDisplay(display); + + return 0; +} +*/ diff --git a/src/systray/docker.h b/src/systray/docker.h new file mode 100644 index 0000000..f60376e --- /dev/null +++ b/src/systray/docker.h @@ -0,0 +1,31 @@ +#ifndef __docker_h +#define __docker_h + +#include +#include + +extern Display *display; +extern Window root, win; +extern GSList *icons; +extern int width, height; +extern int border; +extern gboolean horizontal; +extern int icon_size; +extern gboolean wmaker; + +typedef enum { + KDE = 1, /* kde specific */ + NET /* follows the standard (freedesktop.org) */ +} TrayWindowType; + +typedef struct +{ + TrayWindowType type; + Window id; + int x, y; +} TrayWindow; + +void reposition_icons(); +void fix_geometry(); + +#endif /* __docker_h */ diff --git a/src/systray/icons.c b/src/systray/icons.c new file mode 100644 index 0000000..5c5b240 --- /dev/null +++ b/src/systray/icons.c @@ -0,0 +1,122 @@ +#include "icons.h" +#include "net.h" +#include +#include + +gboolean error; +int window_error_handler(Display *d, XErrorEvent *e) +{ + d=d;e=e; + if (e->error_code == BadWindow) { + error = TRUE; + } else { + g_printerr("X ERROR NOT BAD WINDOW!\n"); + abort(); + } + return 0; +} + + +gboolean icon_swallow(TrayWindow *traywin) +{ + XErrorHandler old; + + error = FALSE; + old = XSetErrorHandler(window_error_handler); + XReparentWindow(display, traywin->id, win, 0, 0); + XSync(display, False); + XSetErrorHandler(old); + + return !error; +} + + +/* + The traywin must have its id and type set. +*/ +gboolean icon_add(Window id, TrayWindowType type) +{ + TrayWindow *traywin; + + assert(id); + assert(type); + + if (wmaker) { + /* do we have room in our window for another icon? */ + unsigned int max = (width / icon_size) * (height / icon_size); + if (g_slist_length(icons) >= max) + return FALSE; /* no room, sorry! REJECTED! */ + } + + traywin = g_new0(TrayWindow, 1); + traywin->type = type; + traywin->id = id; + + if (!icon_swallow(traywin)) { + g_free(traywin); + return FALSE; + } + + /* find the positon for the systray app window */ + if (!wmaker) { + traywin->x = border + (horizontal ? width : 0); + traywin->y = border + (horizontal ? 0 : height); + } else { + int count = g_slist_length(icons); + traywin->x = border + ((width % icon_size) / 2) + + (count % (width / icon_size)) * icon_size; + traywin->y = border + ((height % icon_size) / 2) + + (count / (height / icon_size)) * icon_size; + } + + /* add the new icon to the list */ + icons = g_slist_append(icons, traywin); + + /* watch for the icon trying to resize itself! BAD ICON! BAD! */ + XSelectInput(display, traywin->id, StructureNotifyMask); + + /* position and size the icon window */ + XMoveResizeWindow(display, traywin->id, + traywin->x, traywin->y, icon_size, icon_size); + + /* resize our window so that the new window can fit in it */ + fix_geometry(); + + /* flush before clearing, otherwise the clear isn't effective. */ + XFlush(display); + /* make sure the new child will get the right stuff in its background + for ParentRelative. */ + XClearWindow(display, win); + + /* show the window */ + XMapRaised(display, traywin->id); + + return TRUE; +} + + +void icon_remove(GSList *node) +{ + XErrorHandler old; + TrayWindow *traywin = node->data; + Window traywin_id = traywin->id; + + if (traywin->type == NET) + net_icon_remove(traywin); + + XSelectInput(display, traywin->id, NoEventMask); + + /* remove it from our list */ + g_free(node->data); + icons = g_slist_remove_link(icons, node); + + /* reparent it to root */ + error = FALSE; + old = XSetErrorHandler(window_error_handler); + XReparentWindow(display, traywin_id, root, 0, 0); + XSync(display, False); + XSetErrorHandler(old); + + reposition_icons(); + fix_geometry(); +} diff --git a/src/systray/icons.h b/src/systray/icons.h new file mode 100644 index 0000000..1d2a09c --- /dev/null +++ b/src/systray/icons.h @@ -0,0 +1,13 @@ +#ifndef __icons_h +#define __icons_h + +#include +#include +#include "docker.h" + +extern gboolean error; + +gboolean icon_add(Window id, TrayWindowType type); +void icon_remove(GSList *node); + +#endif /* __icons_h */ diff --git a/src/systray/kde.c b/src/systray/kde.c new file mode 100644 index 0000000..32e1848 --- /dev/null +++ b/src/systray/kde.c @@ -0,0 +1,76 @@ +#include "kde.h" +#include "icons.h" +#include "docker.h" +#include "xproperty.h" +#include +#include + +Atom kde_systray_prop = None; + +void kde_init() +{ + kde_systray_prop = XInternAtom(display, + "_KDE_NET_SYSTEM_TRAY_WINDOWS", False); + assert(kde_systray_prop); + + XSelectInput(display, root, PropertyChangeMask); + kde_update_icons(); +} + +void kde_update_icons() +{ + gulong count = (unsigned) -1; /* grab as many as possible */ + Window *ids; + unsigned int i; + GSList *it, *next; + gboolean removed = FALSE; /* were any removed? */ + + if (! xprop_get32(root, kde_systray_prop, XA_WINDOW, sizeof(Window)*8, + &count, &ids)) + return; + + /* add new windows to our list */ + for (i = 0; i < count; ++i) { + for (it = icons; it != NULL; it = g_slist_next(it)) { + TrayWindow *traywin = it->data; + if (traywin->id == ids[i]) + break; + } + if (!it) + icon_add(ids[i], KDE); + } + + /* remove windows from our list that no longer exist in the property */ + for (it = icons; it != NULL;) { + TrayWindow *traywin = it->data; + gboolean exists; + + if (traywin->type != KDE) { + /* don't go removing non-kde windows */ + exists = TRUE; + } else { + exists = FALSE; + for (i = 0; i < count; ++i) { + if (traywin->id == ids[i]) { + exists = TRUE; + break; + } + } + } + + next = g_slist_next(it); + if (!exists) { + icon_remove(it); + removed =TRUE; + } + it = next; + } + + if (removed) { + /* at least one tray app was removed, so reorganize 'em all and resize*/ + reposition_icons(); + fix_geometry(); + } + + XFree(ids); +} diff --git a/src/systray/kde.h b/src/systray/kde.h new file mode 100644 index 0000000..cb85d3d --- /dev/null +++ b/src/systray/kde.h @@ -0,0 +1,12 @@ +#ifndef __kde_h +#define __kde_h + +#include +#include + +extern Atom kde_systray_prop; + +void kde_update_icons(); +void kde_init(); + +#endif /* __kde_h */ diff --git a/src/systray/net.c b/src/systray/net.c new file mode 100644 index 0000000..9f0c94a --- /dev/null +++ b/src/systray/net.c @@ -0,0 +1,119 @@ +#include "net.h" +#include "docker.h" +#include "icons.h" +#include + +Atom net_opcode_atom; +Window net_sel_win; + +static Atom net_sel_atom; +static Atom net_manager_atom; +static Atom net_message_data_atom; + +/* defined in the systray spec */ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +static void net_create_selection_window() +{ + net_sel_win = XCreateSimpleWindow(display, root, -1, -1, 1, 1, 0, 0, 0); + assert(net_sel_win); +} + + +static void net_destroy_selection_window() +{ + XDestroyWindow(display, net_sel_win); + net_sel_win = None; +} + + +void net_init() +{ + char *name; + XEvent m; + + name = g_strdup_printf("_NET_SYSTEM_TRAY_S%d", DefaultScreen(display)); + net_sel_atom = XInternAtom(display, name, False); + assert(net_sel_atom); + net_opcode_atom = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", False); + assert(net_opcode_atom); + net_manager_atom = XInternAtom(display, "MANAGER", False); + assert(net_manager_atom); + net_message_data_atom = XInternAtom(display, "_NET_SYSTEM_TRAY_MESSAGE_DATA", + False); + assert(net_message_data_atom); + + net_create_selection_window(); + + XSetSelectionOwner(display, net_sel_atom, net_sel_win, CurrentTime); + if (XGetSelectionOwner(display, net_sel_atom) != net_sel_win) + return; /* we don't get the selection */ + + m.type = ClientMessage; + m.xclient.message_type = net_manager_atom; + m.xclient.format = 32; + m.xclient.data.l[0] = CurrentTime; + m.xclient.data.l[1] = net_sel_atom; + m.xclient.data.l[2] = net_sel_win; + m.xclient.data.l[3] = 0; + m.xclient.data.l[4] = 0; + XSendEvent(display, root, False, StructureNotifyMask, &m); +} + + +void net_destroy() +{ + net_destroy_selection_window(); +} + + +void net_message(XClientMessageEvent *e) +{ + unsigned long opcode; + Window id; + + assert(e); + + opcode = e->data.l[1]; + + switch (opcode) + { + case SYSTEM_TRAY_REQUEST_DOCK: /* dock a new icon */ + id = e->data.l[2]; + if (id && icon_add(id, NET)) + XSelectInput(display, id, StructureNotifyMask); + break; + + case SYSTEM_TRAY_BEGIN_MESSAGE: + g_printerr("Message From Dockapp\n"); + id = e->window; + break; + + case SYSTEM_TRAY_CANCEL_MESSAGE: + g_printerr("Message Cancelled\n"); + id = e->window; + break; + + default: + if (opcode == net_message_data_atom) { + g_printerr("Text For Message From Dockapp:\n%s\n", e->data.b); + id = e->window; + break; + } + + /* unknown message type. not in the spec. */ + g_printerr("Warning: Received unknown client message to System Tray " + "selection window.\n"); + break; + } +} + + +void net_icon_remove(TrayWindow *traywin) +{ + assert(traywin); + + XSelectInput(display, traywin->id, NoEventMask); +} diff --git a/src/systray/net.h b/src/systray/net.h new file mode 100644 index 0000000..9d350ea --- /dev/null +++ b/src/systray/net.h @@ -0,0 +1,15 @@ +#ifndef __net_h +#define __net_h + +#include +#include +#include "docker.h" + +extern Window net_sel_win; +extern Atom net_opcode_atom; + +void net_init(); +void net_message(XClientMessageEvent *e); +void net_icon_remove(TrayWindow *traywin); + +#endif /* __net_h */ diff --git a/src/systray/tint_merge.h b/src/systray/tint_merge.h new file mode 100644 index 0000000..475ebac --- /dev/null +++ b/src/systray/tint_merge.h @@ -0,0 +1,21 @@ +/************************************************************************** +* Copyright (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* Merge 'docker' with 'tint2'. +* The goal is to keep unchanged docker code, but without data duplication. +* +**************************************************************************/ + +#ifndef TINT_MERGE_H +#define TINT_MERGE_H + +#define display server.dsp +#define root server.root_win + +/* delete main(), parse_cmd_line() and display_string from docker.c + * include "tint_merge.h" in docker.h + */ + + +#endif + diff --git a/src/systray/version.h b/src/systray/version.h new file mode 100644 index 0000000..0b62be9 --- /dev/null +++ b/src/systray/version.h @@ -0,0 +1 @@ +#define VERSION "1.5" diff --git a/src/systray/version.h.in b/src/systray/version.h.in new file mode 100644 index 0000000..a44b122 --- /dev/null +++ b/src/systray/version.h.in @@ -0,0 +1 @@ +#define VERSION "@VERSION@" diff --git a/src/systray/xproperty.c b/src/systray/xproperty.c new file mode 100644 index 0000000..855379c --- /dev/null +++ b/src/systray/xproperty.c @@ -0,0 +1,64 @@ +#include "xproperty.h" +#include "docker.h" + +gboolean xprop_get8(Window window, Atom atom, Atom type, int size, + gulong *count, guchar **value) +{ + Atom ret_type; + int ret_size; + unsigned long ret_bytes; + int result; + unsigned long nelements = *count; + unsigned long maxread = nelements; + + *value = NULL; + + /* try get the first element */ + result = XGetWindowProperty(display, window, atom, 0l, 1l, False, + AnyPropertyType, &ret_type, &ret_size, + &nelements, &ret_bytes, value); + if (! (result == Success && ret_type == type && + ret_size == size && nelements > 0)) { + if (*value) XFree(*value); + *value = NULL; + nelements = 0; + } else { + /* we didn't the whole property's value, more to get */ + if (! (ret_bytes == 0 || maxread <= nelements)) { + int remain; + + /* get the entire property since it is larger than one element long */ + XFree(*value); + /* + the number of longs that need to be retreived to get the property's + entire value. The last + 1 is the first long that we retrieved above. + */ + remain = (ret_bytes - 1)/sizeof(long) + 1 + 1; + /* dont get more than the max */ + if (remain > size/8 * (signed)maxread) + remain = size/8 * (signed)maxread; + result = XGetWindowProperty(display, window, atom, 0l, remain, + False, type, &ret_type, &ret_size, + &nelements, &ret_bytes, value); + /* + If the property has changed type/size, or has grown since our first + read of it, then stop here and try again. If it shrank, then this will + still work. + */ + if (!(result == Success && ret_type == type && + ret_size == size && ret_bytes == 0)) { + if (*value) XFree(*value); + xprop_get8(window, atom, type, size, count, value); + } + } + } + + *count = nelements; + return *value != NULL; +} + +gboolean xprop_get32(Window window, Atom atom, Atom type, int size, + gulong *count, gulong **value) +{ + return xprop_get8(window, atom, type, size, count, (guchar**)value); +} diff --git a/src/systray/xproperty.h b/src/systray/xproperty.h new file mode 100644 index 0000000..81bc69a --- /dev/null +++ b/src/systray/xproperty.h @@ -0,0 +1,13 @@ +#ifndef __xproperty_h +#define __xproperty_h + +#include +#include + +/* if the func returns TRUE, the returned value must be XFree()'d */ +gboolean xprop_get8(Window window, Atom atom, Atom type, int size, + gulong *count, guchar **value); +gboolean xprop_get32(Window window, Atom atom, Atom type, int size, + gulong *count, gulong **value); + +#endif /* __xproperty_h */ diff --git a/src/taskbar/task.c b/src/taskbar/task.c new file mode 100644 index 0000000..f0b9924 --- /dev/null +++ b/src/taskbar/task.c @@ -0,0 +1,287 @@ +/************************************************************************** +* +* Tint2 : task +* +* Copyright (C) 2007 Pål Staurland (staura@gmail.com) +* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "window.h" +#include "task.h" +#include "server.h" +#include "panel.h" + + + +void add_task (Window win) +{ + Task *new_tsk; + int desktop, monitor; + + if (!win || window_is_hidden (win) || win == window.main_win) return; + + new_tsk = malloc(sizeof(Task)); + new_tsk->win = win; + new_tsk->title = 0; + new_tsk->icon_data = 0; + + get_icon(new_tsk); + get_title(new_tsk); + memcpy(&new_tsk->area, &g_task.area, sizeof(Area)); + memcpy(&new_tsk->area_active, &g_task.area_active, sizeof(Area)); + desktop = window_get_desktop (new_tsk->win); + monitor = window_get_monitor (new_tsk->win); + + //if (panel.mode == MULTI_MONITOR) monitor = window_get_monitor (new_tsk->win); + //else monitor = 0; + //printf("task %s : desktop %d, monitor %d\n", new_tsk->title, desktop, monitor); + + XSelectInput (server.dsp, new_tsk->win, PropertyChangeMask|StructureNotifyMask); + + if (desktop == 0xFFFFFFFF) { + if (new_tsk->title) XFree (new_tsk->title); + if (new_tsk->icon_data) XFree (new_tsk->icon_data); + free(new_tsk); + fprintf(stderr, "task on all desktop : ignored\n"); + return; + } + + Taskbar *tskbar; + tskbar = g_slist_nth_data(panel.area.list, index(desktop, monitor)); + new_tsk->area.parent = tskbar; + tskbar->area.list = g_slist_append(tskbar->area.list, new_tsk); + + if (resize_tasks (tskbar)) + redraw (&tskbar->area); +} + + +void remove_task (Task *tsk) +{ + if (!tsk) return; + + Taskbar *tskbar; + tskbar = (Taskbar*)tsk->area.parent; + tskbar->area.list = g_slist_remove(tskbar->area.list, tsk); + resize_tasks (tskbar); + redraw (&tskbar->area); + + if (tsk->title) XFree (tsk->title); + if (tsk->icon_data) XFree (tsk->icon_data); + XFreePixmap (server.dsp, tsk->area.pmap); + XFreePixmap (server.dsp, tsk->area_active.pmap); + free(tsk); +} + + +void get_title(Task *tsk) +{ + if (!g_task.text) return; + + char *title, *name; + + if (tsk->title) free(tsk->title); + + name = server_get_property (tsk->win, server.atom._NET_WM_VISIBLE_NAME, server.atom.UTF8_STRING, 0); + if (!name || !strlen(name)) { + name = server_get_property (tsk->win, server.atom._NET_WM_NAME, server.atom.UTF8_STRING, 0); + if (!name || !strlen(name)) { + name = server_get_property (tsk->win, server.atom.WM_NAME, XA_STRING, 0); + if (!name || !strlen(name)) { + name = malloc(10); + strcpy(name, "Untitled"); + } + } + } + + // add space before title + title = malloc(strlen(name)+1); + if (g_task.icon) strcpy(title, " "); + else title[0] = 0; + strcat(title, name); + + if (name) XFree (name); + tsk->title = title; +} + + +void get_icon (Task *tsk) +{ + if (!g_task.icon) return; + + long *data; + int num; + + data = server_get_property (tsk->win, server.atom._NET_WM_ICON, XA_CARDINAL, &num); + if (!data) return; + + int w, h; + long *tmp_data; + tmp_data = get_best_icon (data, get_icon_count (data, num), num, &w, &h, g_task.icon_size1); + + tsk->icon_width = w; + tsk->icon_height = h; + tsk->icon_data = malloc (w * h * sizeof (long)); + memcpy (tsk->icon_data, tmp_data, w * h * sizeof (long)); + + XFree (data); +} + + +void draw_task_icon (Task *tsk, int text_width, int active) +{ + if (tsk->icon_data == 0) get_icon (tsk); + if (tsk->icon_data == 0) return; + + Pixmap *pmap; + + if (active) pmap = &tsk->area_active.pmap; + else pmap = &tsk->area.pmap; + + /* Find pos */ + int pos_x; + if (g_task.centered) { + if (g_task.text) + pos_x = (tsk->area.width - text_width - g_task.icon_size1) / 2; + else + pos_x = (tsk->area.width - g_task.icon_size1) / 2; + } + else pos_x = g_task.area.paddingx + g_task.area.border.width; + + /* Render */ + Imlib_Image icon; + Imlib_Color_Modifier cmod; + DATA8 red[256], green[256], blue[256], alpha[256]; + + // TODO: cpu improvement : compute only when icon changed + DATA32 *data; + /* do we have 64bit? => long = 8bit */ + if (sizeof(long) != 4) { + int length = tsk->icon_width * tsk->icon_height; + data = malloc(sizeof(DATA32) * length); + int i; + for (i = 0; i < length; ++i) + data[i] = tsk->icon_data[i]; + } + else data = (DATA32 *) tsk->icon_data; + + icon = imlib_create_image_using_data (tsk->icon_width, tsk->icon_height, data); + imlib_context_set_image (icon); + imlib_context_set_drawable (*pmap); + + cmod = imlib_create_color_modifier (); + imlib_context_set_color_modifier (cmod); + imlib_image_set_has_alpha (1); + imlib_get_color_modifier_tables (red, green, blue, alpha); + + int i, opacity; + if (active) opacity = 255*g_task.font_active.alpha; + else opacity = 255*g_task.font.alpha; + for(i = 127; i < 256; i++) alpha[i] = opacity; + + imlib_set_color_modifier_tables (red, green, blue, alpha); + + //imlib_render_image_on_drawable (pos_x, pos_y); + imlib_render_image_on_drawable_at_size (pos_x, g_task.icon_posy, g_task.icon_size1, g_task.icon_size1); + + imlib_free_color_modifier (); + imlib_free_image (); + if (sizeof(long) != 4) free(data); +} + + +void draw_task_title (cairo_t *c, Task *tsk, int active) +{ + PangoLayout *layout; + config_color *config_text; + int width, height; + + if (g_task.text) { + /* Layout */ + layout = pango_cairo_create_layout (c); + pango_layout_set_font_description (layout, g_task.font_desc); + pango_layout_set_text (layout, tsk->title, -1); + + /* Drawing width and Cut text */ + pango_layout_set_width (layout, ((Taskbar*)tsk->area.parent)->text_width * PANGO_SCALE); + pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); + + /* Center text */ + if (g_task.centered) pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); + else pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT); + + pango_layout_get_pixel_size (layout, &width, &height); + + if (active) config_text = &g_task.font_active; + else config_text = &g_task.font; + + cairo_set_source_rgba (c, config_text->color[0], config_text->color[1], config_text->color[2], config_text->alpha); + + pango_cairo_update_layout (c, layout); + cairo_move_to (c, g_task.text_posx, g_task.text_posy); + pango_cairo_show_layout (c, layout); + + if (g_task.font_shadow) { + cairo_set_source_rgba (c, 0.0, 0.0, 0.0, 0.5); + pango_cairo_update_layout (c, layout); + cairo_move_to (c, g_task.text_posx + 1, g_task.text_posy + 1); + pango_cairo_show_layout (c, layout); + } + g_object_unref (layout); + } + + if (g_task.icon) { + // icon use same opacity as text + draw_task_icon (tsk, width, active); + } +} + + +int draw_foreground_task (void *obj, cairo_t *c) +{ + Task *tsk = obj; + cairo_surface_t *cs; + cairo_t *ca; + + draw_task_title (c, tsk, 0); + + // draw active pmap + if (tsk->area_active.pmap) XFreePixmap (server.dsp, tsk->area_active.pmap); + tsk->area_active.pmap = server_create_pixmap (tsk->area.width, tsk->area.height); + + // add layer of root pixmap + XCopyArea (server.dsp, server.pmap, tsk->area_active.pmap, server.gc, tsk->area.posx, tsk->area.posy, tsk->area.width, tsk->area.height, 0, 0); + + cs = cairo_xlib_surface_create (server.dsp, tsk->area_active.pmap, server.visual, tsk->area.width, tsk->area.height); + ca = cairo_create (cs); + + // redraw task + draw_background (&tsk->area_active, ca); + draw_task_title (ca, tsk, 1); + + cairo_destroy (ca); + cairo_surface_destroy (cs); + return 0; +} + diff --git a/src/taskbar/task.h b/src/taskbar/task.h new file mode 100644 index 0000000..bab6c74 --- /dev/null +++ b/src/taskbar/task.h @@ -0,0 +1,67 @@ +/************************************************************************** +* task : +* - +* +**************************************************************************/ + +#ifndef TASK_H +#define TASK_H + +#include +#include +#include "common.h" + + +// -------------------------------------------------- +// global task parameter +typedef struct { + Area area; + Area area_active; + + int text; + int icon; + int icon_size1; + int centered; + int maximum_width; + int font_shadow; + // icon position + int icon_posy; + // starting position for text ~ task_padding + task_border + icon_size + double text_posx, text_posy; + PangoFontDescription *font_desc; + config_color font; + config_color font_active; +} Global_task; + + + +// -------------------------------------------------- +// task parameter +typedef struct { + // -------------------------------------------------- + // always start with area + Area area; + Area area_active; + + // TODO: group task with list of windows here + Window win; + long *icon_data; + int icon_width; + int icon_height; + char *title; +} Task; + + +Global_task g_task; + + +void add_task (Window win); +void remove_task (Task *tsk); + +int draw_foreground_task (void *obj, cairo_t *c); + +void get_icon (Task *tsk); +void get_title(Task *tsk); + +#endif + diff --git a/src/taskbar/taskbar.c b/src/taskbar/taskbar.c new file mode 100644 index 0000000..05ba366 --- /dev/null +++ b/src/taskbar/taskbar.c @@ -0,0 +1,143 @@ +/************************************************************************** +* +* Tint2 : taskbar +* +* Copyright (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "taskbar.h" +#include "server.h" +#include "window.h" +#include "panel.h" + + + +Task *task_get_task (Window win) +{ + Taskbar *tskbar; + Task *tsk; + GSList *l0; + + for (l0 = panel.area.list; l0 ; l0 = l0->next) { + tskbar = l0->data; + GSList *l1; + for (l1 = tskbar->area.list; l1 ; l1 = l1->next) { + tsk = l1->data; + if (win == tsk->win) return tsk; + } + } + + // nb = panel.nb_desktop * panel.nb_monitor; + //printf("task_get_task return 0\n"); + return 0; +} + + +void task_refresh_tasklist () +{ + Window *win, active_win; + int num_results, i, j; + + win = server_get_property (server.root_win, server.atom._NET_CLIENT_LIST, XA_WINDOW, &num_results); + + if (!win) return; + + /* Remove any old and set active win */ + active_win = window_get_active (); + + Task *tsk; + Taskbar *tskbar; + GSList *l0; + for (l0 = panel.area.list; l0 ; l0 = l0->next) { + tskbar = l0->data; + GSList *l1; + for (l1 = tskbar->area.list; l1 ; l1 = l1->next) { + tsk = l1->data; + + if (tsk->win == active_win) panel.task_active = tsk; + + for (j = 0; j < num_results; j++) { + if (tsk->win == win[j]) break; + } + if (tsk->win != win[j]) remove_task (tsk); + } + } + + /* Add any new */ + for (i = 0; i < num_results; i++) { + if (!task_get_task (win[i])) add_task (win[i]); + } + + XFree (win); +} + + +int resize_tasks (Taskbar *taskbar) +{ + int ret, task_count, pixel_width, modulo_width=0; + int x, taskbar_width; + Task *tsk; + GSList *l; + + // new task width for 'desktop' + task_count = g_slist_length(taskbar->area.list); + if (!task_count) pixel_width = g_task.maximum_width; + else { + taskbar_width = taskbar->area.width - (2 * g_taskbar.border.width) - ((task_count+1) * g_taskbar.paddingx); + + pixel_width = taskbar_width / task_count; + if (pixel_width > g_task.maximum_width) pixel_width = g_task.maximum_width; + else modulo_width = taskbar_width % task_count; + } + + if ((taskbar->task_width == pixel_width) && (taskbar->task_modulo == modulo_width)) { + ret = 0; + } + else { + ret = 1; + taskbar->task_width = pixel_width; + taskbar->task_modulo = modulo_width; + taskbar->text_width = pixel_width - g_task.text_posx - g_task.area.border.width - g_task.area.paddingx; + } + + // change pos_x and width for all tasks + x = taskbar->area.posx + taskbar->area.border.width + taskbar->area.paddingx; + for (l = taskbar->area.list; l ; l = l->next) { + tsk = l->data; + tsk->area.posx = x; + tsk->area_active.posx = x; + tsk->area.width = pixel_width; + tsk->area_active.width = pixel_width; + if (modulo_width) { + tsk->area.width++; + tsk->area_active.width++; + modulo_width--; + } + + x += tsk->area.width + g_taskbar.paddingx; + } + return ret; +} + + diff --git a/src/taskbar/taskbar.h b/src/taskbar/taskbar.h new file mode 100644 index 0000000..3b3560d --- /dev/null +++ b/src/taskbar/taskbar.h @@ -0,0 +1,39 @@ + +#ifndef TASKBAR_H +#define TASKBAR_H + +#include "task.h" + + +// -------------------------------------------------- +// taskbar parameter +typedef struct { + // -------------------------------------------------- + // always start with area + Area area; + + int desktop; + int monitor; + + // task parameters + int task_width; + int task_modulo; + int text_width; +} Taskbar; + + +// -------------------------------------------------- +// global taskbar parameter +Area g_taskbar; + + +Task *task_get_task (Window win); +void task_refresh_tasklist (); + +// return 1 if task_width changed +int resize_tasks (Taskbar *tskbar); + +//void add_taskbar(Area *a); + +#endif + diff --git a/src/tint.c b/src/tint.c new file mode 100644 index 0000000..42581ff --- /dev/null +++ b/src/tint.c @@ -0,0 +1,441 @@ +/************************************************************************** +* +* Tint2 panel +* +* Copyright (C) 2007 Pål Staurland (staura@gmail.com) +* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "server.h" +#include "window.h" +#include "config.h" +#include "task.h" +#include "taskbar.h" +#include "panel.h" +#include "docker.h" +#include "net.h" +#include "kde.h" + + +void signal_handler(int sig) +{ + // signal handler is light as it should be + panel.signal_pending = sig; +} + + +void init () +{ + // Set signal handler + signal(SIGUSR1, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + // set global data + memset(&panel, 0, sizeof(Panel)); + memset(&server, 0, sizeof(Server_global)); + memset(&g_task, 0, sizeof(Global_task)); + memset(&g_taskbar, 0, sizeof(Area)); + panel.clock.area.draw_foreground = draw_foreground_clock; + g_task.area.draw_foreground = draw_foreground_task; + window.main_win = 0; + + // append full transparency background + //Area *back = calloc(1, sizeof(Area)); + list_back = g_slist_append(0, calloc(1, sizeof(Area))); + + server.dsp = XOpenDisplay (NULL); + if (!server.dsp) { + fprintf(stderr, "Could not open display.\n"); + exit(0); + } + server_init_atoms (); + server.screen = DefaultScreen (server.dsp); + server.root_win = RootWindow (server.dsp, server.screen); + server.depth = DefaultDepth (server.dsp, server.screen); + server.visual = DefaultVisual (server.dsp, server.screen); + server.desktop = server_get_current_desktop (); + + XSetErrorHandler ((XErrorHandler) server_catch_error); + + // init systray + display = server.dsp; + root = RootWindow(display, DefaultScreen(display)); + //create_main_window(); + //kde_init(); + //net_init(); + //printf("ici 4\n"); + + imlib_context_set_display (server.dsp); + imlib_context_set_visual (server.visual); + imlib_context_set_colormap (DefaultColormap (server.dsp, server.screen)); + + /* Catch events */ + XSelectInput (server.dsp, server.root_win, PropertyChangeMask|StructureNotifyMask); + + setlocale(LC_ALL, ""); +} + + +void window_action (Task *tsk, int action) +{ + switch (action) { + case CLOSE: + set_close (tsk->win); + break; + case TOGGLE: + set_active(tsk->win); + break; + case ICONIFY: + XIconifyWindow (server.dsp, tsk->win, server.screen); + break; + case TOGGLE_ICONIFY: + if (tsk == panel.task_active) XIconifyWindow (server.dsp, tsk->win, server.screen); + else set_active (tsk->win); + break; + case SHADE: + window_toggle_shade (tsk->win); + break; + } +} + + +void event_button_press (int x, int y) +{ + if (panel.mode == SINGLE_DESKTOP) { + // drag and drop disabled + XLowerWindow (server.dsp, window.main_win); + return; + } + + Taskbar *tskbar; + GSList *l0; + for (l0 = panel.area.list; l0 ; l0 = l0->next) { + tskbar = l0->data; + if (x >= tskbar->area.posx && x <= (tskbar->area.posx + tskbar->area.width)) + break; + } + + if (l0) { + Task *tsk; + for (l0 = tskbar->area.list; l0 ; l0 = l0->next) { + tsk = l0->data; + if (x >= tsk->area.posx && x <= (tsk->area.posx + tsk->area.width)) { + panel.task_drag = tsk; + break; + } + } + } + + XLowerWindow (server.dsp, window.main_win); +} + + +void event_button_release (int button, int x, int y) +{ + int action = TOGGLE_ICONIFY; + + switch (button) { + case 2: + action = panel.mouse_middle; + break; + case 3: + action = panel.mouse_right; + break; + case 4: + action = panel.mouse_scroll_up; + break; + case 5: + action = panel.mouse_scroll_down; + break; + } + + // TODO: ne pas afficher les taskbar invisibles + //if (panel.mode != MULTI_DESKTOP && desktop != server.desktop) continue; + + // search taskbar + Taskbar *tskbar; + GSList *l0; + for (l0 = panel.area.list; l0 ; l0 = l0->next) { + tskbar = l0->data; + if (x >= tskbar->area.posx && x <= (tskbar->area.posx + tskbar->area.width)) + goto suite; + } + + // TODO: check better solution to keep window below + XLowerWindow (server.dsp, window.main_win); + panel.task_drag = 0; + return; + +suite: + // drag and drop task + if (panel.task_drag) { + if (tskbar != panel.task_drag->area.parent && action == TOGGLE_ICONIFY) { + windows_set_desktop(panel.task_drag->win, tskbar->desktop); + if (tskbar->desktop == server.desktop) + set_active(panel.task_drag->win); + panel.task_drag = 0; + return; + } + else panel.task_drag = 0; + } + + // switch desktop + if (panel.mode == MULTI_DESKTOP) + if (tskbar->desktop != server.desktop && action != CLOSE) + set_desktop (tskbar->desktop); + + // action on task + Task *tsk; + GSList *l; + for (l = tskbar->area.list ; l ; l = l->next) { + tsk = l->data; + if (x >= tsk->area.posx && x <= (tsk->area.posx + tsk->area.width)) { + window_action (tsk, action); + break; + } + } + + // to keep window below + XLowerWindow (server.dsp, window.main_win); +} + + +void event_property_notify (Window win, Atom at) +{ + + if (win == server.root_win) { + if (!server.got_root_win) { + XSelectInput (server.dsp, server.root_win, PropertyChangeMask|StructureNotifyMask); + server.got_root_win = 1; + } + + /* Change number of desktops */ + else if (at == server.atom._NET_NUMBER_OF_DESKTOPS) { + config_taskbar(); + redraw(&panel.area); + panel.refresh = 1; + } + /* Change desktop */ + else if (at == server.atom._NET_CURRENT_DESKTOP) { + server.desktop = server_get_current_desktop (); + if (panel.mode != MULTI_DESKTOP) panel.refresh = 1; + } + /* Window list */ + else if (at == server.atom._NET_CLIENT_LIST) { + task_refresh_tasklist (); + panel.refresh = 1; + } + /* Active */ + else if (at == server.atom._NET_ACTIVE_WINDOW) { + Window w1 = window_get_active (); + Task *t = task_get_task(w1); + if (t) panel.task_active = t; + else { + Window w2; + if (XGetTransientForHint(server.dsp, w1, &w2) != 0) + if (w2) panel.task_active = task_get_task(w2); + } + panel.refresh = 1; + } + /* Wallpaper changed */ + else if (at == server.atom._XROOTPMAP_ID) { + XFreePixmap (server.dsp, server.root_pmap); + server.root_pmap = 0; + redraw(&panel.area); + panel.clock.area.redraw = 1; + panel.refresh = 1; + } + } + else { + Task *tsk; + tsk = task_get_task (win); + if (!tsk) return; + //printf("atom root_win = %s, %s\n", XGetAtomName(server.dsp, at), tsk->title); + + /* Window title changed */ + if (at == server.atom._NET_WM_VISIBLE_NAME || at == server.atom._NET_WM_NAME || at == server.atom.WM_NAME) { + get_title(tsk); + tsk->area.redraw = 1; + panel.refresh = 1; + } + /* Iconic state */ + else if (at == server.atom.WM_STATE) { + if (window_is_iconified (win)) + if (panel.task_active == tsk) panel.task_active = 0; + } + /* Window icon changed */ + else if (at == server.atom._NET_WM_ICON) { + if (tsk->icon_data != 0) XFree (tsk->icon_data); + tsk->area.redraw = 1; + tsk->icon_data = 0; + panel.refresh = 1; + } + /* Window desktop changed */ + else if (at == server.atom._NET_WM_DESKTOP) { + add_task (tsk->win); + remove_task (tsk); + panel.refresh = 1; + } + + if (!server.got_root_win) server.root_win = RootWindow (server.dsp, server.screen); + } +} + + +void event_configure_notify (Window win) +{ + Task *tsk; + + tsk = task_get_task (win); + if (!tsk) return; + +/* TODO ??? voir ancien code !! + Taskbar *tskbar; + tskbar = tsk->area.parent; + int new_monitor = window_get_monitor (win); + int desktop = tskbar->desktop; + + // task on the same monitor + if (tsk->id_taskbar == index(desktop, new_monitor)) return; + + add_task (tsk->win); + remove_task (tsk); + panel.refresh = 1; + */ +} + + +void event_timer() +{ + struct timeval stv; + + if (!panel.clock.time1_format) return; + + if (gettimeofday(&stv, 0)) return; + + if (abs(stv.tv_sec - panel.clock.clock.tv_sec) < panel.clock.time_precision) return; + + // update clock + panel.clock.clock.tv_sec = stv.tv_sec; + panel.clock.clock.tv_sec -= panel.clock.clock.tv_sec % panel.clock.time_precision; + panel.clock.area.redraw = 1; + panel.refresh = 1; +} + + +int main (int argc, char *argv[]) +{ + XEvent e; + fd_set fd; + int x11_fd, i, c; + struct timeval tv; + + c = getopt (argc, argv, "c:"); + init (); + +load_config: + if (server.root_pmap) XFreePixmap (server.dsp, server.root_pmap); + server.root_pmap = 0; + // read tint2rc config + i = 0; + if (c != -1) + i = config_read_file (optarg); + if (!i) + i = config_read (); + if (!i) { + fprintf(stderr, "usage: tint2 [-c] \n"); + cleanup(); + exit(1); + } + config_finish (); + + window_draw_panel (); + + x11_fd = ConnectionNumber (server.dsp); + XSync (server.dsp, False); + + while (1) { + // thanks to AngryLlama for the timer + // Create a File Description Set containing x11_fd + FD_ZERO (&fd); + FD_SET (x11_fd, &fd); + + tv.tv_usec = 500000; + tv.tv_sec = 0; + + // Wait for X Event or a Timer + if (select(x11_fd+1, &fd, 0, 0, &tv)) { + while (XPending (server.dsp)) { + XNextEvent(server.dsp, &e); + + switch (e.type) { + case ButtonPress: + if (e.xbutton.button == 1) event_button_press (e.xbutton.x, e.xbutton.y); + break; + + case ButtonRelease: + event_button_release (e.xbutton.button, e.xbutton.x, e.xbutton.y); + break; + + case Expose: + XCopyArea (server.dsp, server.pmap, window.main_win, server.gc, 0, 0, panel.area.width, panel.area.height, 0, 0); + break; + + case PropertyNotify: + event_property_notify (e.xproperty.window, e.xproperty.atom); + break; + + case ConfigureNotify: + if (e.xconfigure.window == server.root_win) + goto load_config; + else + if (panel.mode == MULTI_MONITOR) + event_configure_notify (e.xconfigure.window); + break; + } + } + } + else event_timer(); + + switch (panel.signal_pending) { + case SIGUSR1: + goto load_config; + case SIGINT: + case SIGTERM: + cleanup (); + return 0; + } + + if (panel.refresh && !panel.sleep_mode) { + visual_refresh (); + //printf(" *** visual_refresh\n"); + } + } +} + + diff --git a/src/tint2 b/src/tint2 new file mode 100755 index 0000000..08a6116 Binary files /dev/null and b/src/tint2 differ diff --git a/src/util/area.c b/src/util/area.c new file mode 100644 index 0000000..2f44cf5 --- /dev/null +++ b/src/util/area.c @@ -0,0 +1,182 @@ +/************************************************************************** +* +* Tint2 : area +* +* Copyright (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "window.h" +#include "server.h" +#include "area.h" + + + +void redraw (Area *a) +{ + a->redraw = 1; + + GSList *l; + for (l = a->list ; l ; l = l->next) + redraw(l->data); +} + + +int draw (Area *a) +{ + if (!a->redraw) return 0; + + cairo_surface_t *cs; + cairo_t *c; + int ret = 0; + + if (a->pmap) XFreePixmap (server.dsp, a->pmap); + a->pmap = server_create_pixmap (a->width, a->height); + + // add layer of root pixmap + XCopyArea (server.dsp, server.pmap, a->pmap, server.gc, a->posx, a->posy, a->width, a->height, 0, 0); + + cs = cairo_xlib_surface_create (server.dsp, a->pmap, server.visual, a->width, a->height); + c = cairo_create (cs); + + draw_background (a, c); + + if (a->draw_foreground) { + ret = a->draw_foreground(a, c); + } + else { + // parcours de la liste des sous objets + } + + cairo_destroy (c); + cairo_surface_destroy (cs); + a->redraw = 0; + + return ret; +} + + +void draw_background (Area *a, cairo_t *c) +{ + if (a->back.alpha > 0.0) { + //printf(" draw_background %d %d\n", a->width, a->height); + draw_rect(c, a->border.width, a->border.width, a->width-(2.0 * a->border.width), a->height-(2.0*a->border.width), a->border.rounded - a->border.width/1.571); + /* + double x0, y0, x1, y1; + x0 = 0; + y0 = 100; + x1 = 100; + y1 = 0; + + cairo_pattern_t *linpat; + cairo_matrix_t matrix; + linpat = cairo_pattern_create_linear (x0, y0, x1, y1); + + cairo_pattern_add_color_stop_rgba (linpat, 0, a->back.color[0], a->back.color[1], a->back.color[2], a->back.alpha); + cairo_pattern_add_color_stop_rgba (linpat, 1, a->back.color[0], a->back.color[1], a->back.color[2], 0); + //cairo_matrix_init_scale (&matrix, a->height, a->width); + //cairo_pattern_set_matrix (linpat, &matrix); + cairo_set_source (c, linpat); + */ + cairo_set_source_rgba(c, a->back.color[0], a->back.color[1], a->back.color[2], a->back.alpha); + + cairo_fill(c); + //cairo_pattern_destroy (linpat); + } + + if (a->border.width > 0 && a->border.alpha > 0.0) { + cairo_set_line_width (c, a->border.width); + + // draw border inside (x, y, width, height) + draw_rect(c, a->border.width/2.0, a->border.width/2.0, a->width - a->border.width, a->height - a->border.width, a->border.rounded); + /* + // convert : radian = degre * M_PI/180 + // définir le dégradé dans un carré de (0,0) (100,100) + // ensuite ce dégradé est extrapolé selon le ratio width/height + // dans repère (0, 0) (100, 100) + double X0, Y0, X1, Y1, degre; + // x = X * (a->width / 100), y = Y * (a->height / 100) + double x0, y0, x1, y1; + X0 = 0; + Y0 = 100; + X1 = 100; + Y1 = 0; + degre = 45; + // et ensuite faire la changement d'unité du repère + // car ce qui doit resté inchangée est les traits et pas la direction + + // il faut d'abord appliquer une rotation de 90° (et -180° si l'angle est supérieur à 180°) + // ceci peut être appliqué une fois pour toute au départ + // ensuite calculer l'angle dans le nouveau repère + // puis faire une rotation de 90° + x0 = X0 * ((double)a->width / 100); + x1 = X1 * ((double)a->width / 100); + y0 = Y0 * ((double)a->height / 100); + y1 = Y1 * ((double)a->height / 100); + + x0 = X0 * ((double)a->height / 100); + x1 = X1 * ((double)a->height / 100); + y0 = Y0 * ((double)a->width / 100); + y1 = Y1 * ((double)a->width / 100); + printf("repère (%d, %d) points (%lf, %lf) (%lf, %lf)\n", a->width, a->height, x0, y0, x1, y1); + + cairo_pattern_t *linpat; + linpat = cairo_pattern_create_linear (x0, y0, x1, y1); + cairo_pattern_add_color_stop_rgba (linpat, 0, a->border.color[0], a->border.color[1], a->border.color[2], a->border.alpha); + cairo_pattern_add_color_stop_rgba (linpat, 1, a->border.color[0], a->border.color[1], a->border.color[2], 0); + cairo_set_source (c, linpat); + */ + cairo_set_source_rgba (c, a->border.color[0], a->border.color[1], a->border.color[2], a->border.alpha); + + cairo_stroke (c); + //cairo_pattern_destroy (linpat); + } +} + + +void refresh (Area *a) +{ + XCopyArea (server.dsp, a->pmap, server.pmap, server.gc, 0, 0, a->width, a->height, a->posx, a->posy); +} + + +void remove_area (Area *a) +{ + Area *parent; + + parent = (Area*)a->parent; + parent->list = g_slist_remove(parent->list, a); + redraw (parent); + +} + + +void add_area (Area *a) +{ + Area *parent; + + parent = (Area*)a->parent; + parent->list = g_slist_remove(parent->list, a); + redraw (parent); + +} + diff --git a/src/util/area.h b/src/util/area.h new file mode 100644 index 0000000..2ad26aa --- /dev/null +++ b/src/util/area.h @@ -0,0 +1,140 @@ +/************************************************************************** +* base class for all objects (panel, taskbar, task, systray, clock, ...). +* each object 'inherit' Area and implement draw_foreground if needed. +* +* Area is at the begining of each object so &object == &area. +* +* une zone comprend : +* - fond : couleur / opacité +* - contenu +* - largeur / hauteur +* - paddingx / paddingy +* - pixmap mémorisant l'affichage (évite de redessiner l'objet à chaque rafraichissement) +* - une liste de sous objets +* +* un objet comprend les actions: +* 1) redraw(obj) +* force l'indicateur 'redraw' sur l'objet +* parcoure la liste des sous objets => redraw(obj) +* 2) draw(obj) +* dessine le background, dessine le contenu dans pmap +* parcoure la liste des sous objets => draw(obj) +* le pmap de l'objet se base sur le pmap de l'objet parent (cumul des couches) +* 3) draw_background(obj) +* dessine le fond dans pmap +* 4) draw_foreground(obj) = 0 : fonction virtuelle à redéfinir +* dessine le contenu dans pmap +* si l'objet n'a pas de contenu, la fonction est nulle +* 5) resize_width(obj, width) = 0 : fonction virtuelle à redéfinir +* recalcule la largeur de l'objet (car la hauteur est fixe) +* - taille systray calculée à partir de la liste des icones +* - taille clock calculée à partir de l'heure +* - taille d'une tache calculée à partir de la taskbar (ajout, suppression, taille) +* - taille d'une taskbar calculée à partir de la taille du panel et des autres objets +* 6) voir refresh(obj) +* +* Implémentation : +* - tous les éléments du panel possèdent 1 objet en début de structure +* panel, taskbar, systray, task, ... +* - l'objet est en fait une zone (area). +* l'imbrication des sous objet doit permettre de gérer le layout. +* - on a une relation 1<->1 entre un objet et une zone graphique +* les taskbar affichent toutes les taches. +* donc on utilise la liste des objets pour gérer la liste des taches. +* - les taches ont 2 objets : l'un pour la tache inactive et l'autre pour la tache active +* draw(obj) est appellé sur le premier objet automatiquement +* et draw_foreground(obj) lance l'affichage du 2 ieme objet +* ainsi la taskbar gère bien une liste d'objets mais draw(obj) dessine les 2 objets +* - les fonctions de refresh et de draw sont totalement dissociées +* +* ---------------------------------------------------- +* A évaluer : +* 1. voir comment définir et gérer le panel_layout avec les objets +* => peut on s'affranchir des données spécifiques à chaque objet ? +* => comment gérer l'affichage du layout ? +* => comment configurer le layout ? +* => voir le cumul des couches et l'imbrication entre objet et parent ? +* 2. voir la fonction de refresh des objets ?? +* surtout le refresh des taches qui est différent pour la tache active +* +* 3. tester l'implémentation et évaluer les autres abstractions possibles ? +* +* 4. comment gérer le groupage des taches +* 5. la clock est le contenu du panel. mais elle ne tiens pas compte du padding vertical ? +* c'est ok pour la clock. voir l'impact sur paddingx ? +* +* voir resize_taskbar(), resize_clock() et resize_tasks() +* voir les taches actives et inactives ?? une seule tache est active ! +* variable widthChanged ou bien emission d'un signal ??? +* +* 6) config(obj) configure un objet (définie les positions verticales) +* +**************************************************************************/ + +#ifndef AREA_H +#define AREA_H + +#include +#include + +#include "common.h" + + + +typedef struct +{ + double color[3]; + double alpha; + int width; + int rounded; +} Border; + + +typedef struct +{ + double color[3]; + double alpha; +} Color; + + +typedef struct { + // need redraw Pixmap + int redraw; + + int paddingx, paddingy; + int width, height; + Pixmap pmap; + + Color back; + Border border; + + // absolute coordinate in panel + int posx, posy; + // parent Area + void *parent; + + // pointer to function + // draw_foreground : return 1 if width changed, return O otherwise + int (*draw_foreground)(void *obj, cairo_t *c); + void (*add_child)(void *obj); + int (*remove_child)(void *obj); + + // list of child + GSList *list; +} Area; + + +// redraw an area and childs +void redraw (Area *a); + +// draw background and foreground +// return 1 if width changed, return O otherwise +int draw (Area *a); +void draw_background (Area *a, cairo_t *c); + +void refresh (Area *a); +void remove_area (Area *a); +void add_area (Area *a); + +#endif + diff --git a/src/util/common.h b/src/util/common.h new file mode 100644 index 0000000..313a3b7 --- /dev/null +++ b/src/util/common.h @@ -0,0 +1,41 @@ +/************************************************************************** +* Common declarations +* +**************************************************************************/ + +#ifndef COMMON_H +#define COMMON_H + + +#define WM_CLASS_TINT "panel" + +#include "area.h" + +// taskbar table : convert 2 dimension in 1 dimension +#define index(i, j) ((i * panel.nb_monitor) + j) + +// mouse actions +enum { NONE=0, CLOSE, TOGGLE, ICONIFY, SHADE, TOGGLE_ICONIFY }; + + + +typedef struct config_border +{ + double color[3]; + double alpha; + int width; + int rounded; +} config_border; + + +typedef struct config_color +{ + double color[3]; + double alpha; +} config_color; + + + + +#endif + diff --git a/src/util/window.c b/src/util/window.c new file mode 100644 index 0000000..ebbdce0 --- /dev/null +++ b/src/util/window.c @@ -0,0 +1,264 @@ +/************************************************************************** +* +* Tint2 : common windows function +* +* Copyright (C) 2007 Pål Staurland (staura@gmail.com) +* Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "window.h" +#include "server.h" + + + +void set_active (Window win) +{ + send_event32 (win, server.atom._NET_ACTIVE_WINDOW, 2, 0); +} + + +void set_desktop (int desktop) +{ + send_event32 (server.root_win, server.atom._NET_CURRENT_DESKTOP, desktop, 0); +} + + +void windows_set_desktop (Window win, int desktop) +{ + send_event32 (win, server.atom._NET_WM_DESKTOP, desktop, 2); +} + + +void set_close (Window win) +{ + send_event32 (win, server.atom._NET_CLOSE_WINDOW, 0, 2); +} + + +void window_toggle_shade (Window win) +{ + send_event32 (win, server.atom._NET_WM_STATE, 2, 0); +} + + +int window_is_hidden (Window win) +{ + Window window; + Atom *at; + int count, i; + + if (XGetTransientForHint(server.dsp, win, &window) != 0) { + if (window) { + return 1; + } + } + + at = server_get_property (win, server.atom._NET_WM_STATE, XA_ATOM, &count); + for (i = 0; i < count; i++) { + if (at[i] == server.atom._NET_WM_STATE_SKIP_PAGER || at[i] == server.atom._NET_WM_STATE_SKIP_TASKBAR) { + XFree(at); + return 1; + } + } + XFree(at); + + at = server_get_property (win, server.atom._NET_WM_WINDOW_TYPE, XA_ATOM, &count); + for (i = 0; i < count; i++) { + if (at[i] == server.atom._NET_WM_WINDOW_TYPE_DOCK || at[i] == server.atom._NET_WM_WINDOW_TYPE_DESKTOP || at[i] == server.atom._NET_WM_WINDOW_TYPE_TOOLBAR || at[i] == server.atom._NET_WM_WINDOW_TYPE_MENU || at[i] == server.atom._NET_WM_WINDOW_TYPE_SPLASH) { + XFree(at); + return 1; + } + } + + // specification + // Windows with neither _NET_WM_WINDOW_TYPE nor WM_TRANSIENT_FOR set + // MUST be taken as top-level window. + XFree(at); + return 0; +} + + +int window_get_desktop (Window win) +{ + return get_property32(win, server.atom._NET_WM_DESKTOP, XA_CARDINAL); +} + + +int window_get_monitor (Window win) +{ + int i, x, y; + Window src; + + XTranslateCoordinates(server.dsp, win, server.root_win, 0, 0, &x, &y, &src); + for (i = 0; i < server.nb_monitor; i++) { + if (x >= server.monitor[i].x && x <= (server.monitor[i].x + server.monitor[i].width)) + if (y >= server.monitor[i].y && y <= (server.monitor[i].y + server.monitor[i].height)) + break; + } + + //printf("window %lx : ecran %d, (%d, %d)\n", win, i, x, y); + if (i == server.nb_monitor) return 0; + else return i; +} + + +int window_is_iconified (Window win) +{ + return (IconicState == get_property32(win, server.atom.WM_STATE, server.atom.WM_STATE)); +} + + +int server_get_number_of_desktop () +{ + return get_property32(server.root_win, server.atom._NET_NUMBER_OF_DESKTOPS, XA_CARDINAL); +} + + +int server_get_current_desktop () +{ + return get_property32(server.root_win, server.atom._NET_CURRENT_DESKTOP, XA_CARDINAL); +} + + +Window window_get_active () +{ + return get_property32(server.root_win, server.atom._NET_ACTIVE_WINDOW, XA_WINDOW); +} + + +int window_is_active (Window win) +{ + return (win == get_property32(server.root_win, server.atom._NET_ACTIVE_WINDOW, XA_WINDOW)); +} + + +int get_icon_count (long *data, int num) +{ + int count, pos, w, h; + + count = 0; + pos = 0; + while (pos < num) { + w = data[pos++]; + h = data[pos++]; + pos += w * h; + if (pos > num || w * h == 0) break; + count++; + } + + return count; +} + + +long *get_best_icon (long *data, int icon_count, int num, int *iw, int *ih, int best_icon_size) +{ + int width[icon_count], height[icon_count], pos, i, w, h; + long *icon_data[icon_count]; + + /* List up icons */ + pos = 0; + i = icon_count; + while (i--) { + w = data[pos++]; + h = data[pos++]; + if (pos + w * h > num) break; + + width[i] = w; + height[i] = h; + icon_data[i] = &data[pos]; + + pos += w * h; + } + + /* Try to find exact size */ + int icon_num = -1; + for (i = 0; i < icon_count; i++) { + if (width[i] == best_icon_size) { + icon_num = i; + break; + } + } + + /* Take the biggest or whatever */ + if (icon_num < 0) { + int highest = 0; + for (i = 0; i < icon_count; i++) { + if (width[i] > highest) { + icon_num = i; + highest = width[i]; + } + } + } + + *iw = width[icon_num]; + *ih = height[icon_num]; + return icon_data[icon_num]; +} + + +void draw_rect(cairo_t *c, double x, double y, double w, double h, double r) +{ + if (r > 0.0) { + double c1 = 0.55228475 * r; + + cairo_move_to(c, x+r, y); + cairo_rel_line_to(c, w-2*r, 0); + cairo_rel_curve_to(c, c1, 0.0, r, c1, r, r); + cairo_rel_line_to(c, 0, h-2*r); + cairo_rel_curve_to(c, 0.0, c1, c1-r, r, -r, r); + cairo_rel_line_to (c, -w +2*r, 0); + cairo_rel_curve_to (c, -c1, 0, -r, -c1, -r, -r); + cairo_rel_line_to (c, 0, -h + 2 * r); + cairo_rel_curve_to (c, 0, -c1, r - c1, -r, r, -r); + } + else + cairo_rectangle(c, x, y, w, h); +} + + +void get_text_size(PangoFontDescription *font, int *height_ink, int *height, int panel_height, char *text, int len) +{ + PangoRectangle rect_ink, rect; + + Pixmap pmap = server_create_pixmap (panel_height, panel_height); + cairo_surface_t *cs = cairo_xlib_surface_create (server.dsp, pmap, server.visual, panel_height, panel_height); + cairo_t *c = cairo_create (cs); + + PangoLayout *layout = pango_cairo_create_layout (c); + pango_layout_set_font_description (layout, font); + pango_layout_set_text (layout, text, len); + + pango_layout_get_pixel_extents(layout, &rect_ink, &rect); + *height_ink = rect_ink.height; + *height = rect.height; + //printf("dimension : %d - %d\n", rect_ink.height, rect.height); + + g_object_unref (layout); + cairo_destroy (c); + cairo_surface_destroy (cs); + XFreePixmap (server.dsp, pmap); +} + + + diff --git a/src/util/window.h b/src/util/window.h new file mode 100644 index 0000000..fd596c0 --- /dev/null +++ b/src/util/window.h @@ -0,0 +1,46 @@ +/************************************************************************** +* window : +* - +* +* Check COPYING file for Copyright +* +**************************************************************************/ + +#ifndef WINDOW_H +#define WINDOW_H + +#include +#include +#include + + +typedef struct window_global +{ + Window main_win; +} window_global; + +window_global window; + +void set_active (Window win); +void set_desktop (int desktop); +void set_close (Window win); +int server_get_current_desktop (); +int server_get_number_of_desktop (); +int window_is_iconified (Window win); +int window_is_hidden (Window win); +int window_is_active (Window win); +int get_icon_count (long *data, int num); +long *get_best_icon (long *data, int icon_count, int num, int *iw, int *ih, int best_icon_size); +void window_toggle_shade (Window win); +int window_get_desktop (Window win); +void windows_set_desktop (Window win, int desktop); +int window_get_monitor (Window win); +Window window_get_active (); + +// draw rounded rectangle +void draw_rect(cairo_t *c, double x, double y, double w, double h, double r); + +void get_text_size(PangoFontDescription *font, int *height_ink, int *height, int panel_height, char *text, int len); + + +#endif diff --git a/tintrc03 b/tintrc03 new file mode 100644 index 0000000..914dfd1 --- /dev/null +++ b/tintrc03 @@ -0,0 +1,75 @@ +#--------------------------------------------- +# TINT CONFIG FILE +#--------------------------------------------- + +#--------------------------------------------- +# BACKGROUND AND BORDER +#--------------------------------------------- +rounded = 1 +border_width = 1 +background_color = #282828 100 +border_color = #000000 100 + +rounded = 1 +border_width = 1 +background_color = #282828 100 +#background_color = #3b3b3b 100 +border_color = #cccccc 100 + +#--------------------------------------------- +# PANEL +#--------------------------------------------- +panel_monitor = 1 +panel_position = bottom right +panel_size = 0 27 +panel_margin = 0 0 +panel_padding = 3 2 +font_shadow = 0 +panel_background_id = 1 + +#--------------------------------------------- +# TASKBAR +#--------------------------------------------- +taskbar_mode = multi_desktop +taskbar_padding = 4 0 +taskbar_background_id = 0 + +#--------------------------------------------- +# TASKS +#--------------------------------------------- +task_icon = 1 +task_text = 1 +task_width = 200 +task_centered = 1 +task_padding = 1 3 +task_font = sans 8 +task_font_color = #ffffff 40 +task_active_font_color = #ffffff 100 +task_background_id = 0 +task_active_background_id = 2 + +#--------------------------------------------- +# SYSTRAY +#--------------------------------------------- +#systray_padding = 9 3 +#systray_background_id = 0 + +#--------------------------------------------- +# CLOCK +#--------------------------------------------- +time1_format = %H:%M:%S +time1_font = sans 7 +time2_format = %A %d %B +time2_font = sans 7 +clock_font_color = #ffffff 100 +clock_padding = 0 0 +clock_background_id = 0 + +#--------------------------------------------- +# MOUSE ACTION ON TASK +#--------------------------------------------- +mouse_middle = none +mouse_right = close +mouse_scroll_up = toggle +mouse_scroll_down = iconify + diff --git a/tintrc04 b/tintrc04 new file mode 100644 index 0000000..fd930d4 --- /dev/null +++ b/tintrc04 @@ -0,0 +1,79 @@ +#--------------------------------------------- +# TINT CONFIG FILE +#--------------------------------------------- + +#--------------------------------------------- +# BACKGROUND AND BORDER +#--------------------------------------------- +rounded = 5 +border_width = 1 +background_color = #ffffff 40 +border_color = #ffffff 70 + +rounded = 4 +border_width = 0 +background_color = #ffffff 0 +border_color = #d1d1d1 0 + +rounded = 4 +border_width = 0 +background_color = #ffffff 30 +border_color = #d1d1d1 14 + +#--------------------------------------------- +# PANEL +#--------------------------------------------- +panel_monitor = 1 +panel_position = bottom center +panel_size = 1000 25 +panel_margin = 0 0 +panel_padding = 6 0 +font_shadow = 0 +panel_background_id = 1 + +#--------------------------------------------- +# TASKBAR +#--------------------------------------------- +taskbar_mode = multi_desktop +taskbar_padding = 2 3 +taskbar_background_id = 0 + +#--------------------------------------------- +# TASKS +#--------------------------------------------- +task_icon = 1 +task_text = 1 +task_width = 150 +task_centered = 1 +task_padding = 3 2 +task_font = myriad pro 8 +task_font_color = #000000 70 +task_active_font_color = #000000 100 +task_background_id = 2 +task_active_background_id = 3 + +#--------------------------------------------- +# SYSTRAY +#--------------------------------------------- +#systray_padding = 9 3 +#systray_background_id = 0 + +#--------------------------------------------- +# CLOCK +#--------------------------------------------- +time1_format = %H:%M +time1_font = sans bold 12 +#time2_format = %A %d %B +#time2_font = sans bold 10 +clock_font_color = #000000 70 +clock_padding = 6 0 +clock_background_id = 0 + +#--------------------------------------------- +# MOUSE ACTION ON TASK +#--------------------------------------------- +mouse_middle = none +mouse_right = close +mouse_scroll_up = toggle +mouse_scroll_down = iconify + diff --git a/tintrc05 b/tintrc05 new file mode 100644 index 0000000..5c7378b --- /dev/null +++ b/tintrc05 @@ -0,0 +1,74 @@ +#--------------------------------------------- +# TINT CONFIG FILE +#--------------------------------------------- + +#--------------------------------------------- +# BACKGROUND AND BORDER +#--------------------------------------------- +rounded = 3 +border_width = 1 +background_color = #3c3020 90 +border_color = #3c3020 90 + +rounded = 3 +border_width = 1 +background_color = #3c3020 90 +border_color = #ffffff 30 + +#--------------------------------------------- +# PANEL +#--------------------------------------------- +panel_monitor = 1 +panel_position = bottom center +panel_size = 900 30 +panel_margin = 0 0 +panel_padding = 10 2 +font_shadow = 0 +panel_background_id = 0 + +#--------------------------------------------- +# TASKBAR +#--------------------------------------------- +taskbar_mode = single_desktop +taskbar_padding = 9 0 +taskbar_background_id = 0 + +#--------------------------------------------- +# TASKS +#--------------------------------------------- +task_icon = 0 +task_text = 1 +task_width = 190 +task_centered = 1 +task_padding = 2 0 +task_font = sans 8.4 +task_font_color = #ececec 50 +task_active_font_color = #ffffff 90 +task_background_id = 1 +task_active_background_id = 2 + +#--------------------------------------------- +# SYSTRAY +#--------------------------------------------- +#systray_padding = 9 3 +#systray_background_id = 0 + +#--------------------------------------------- +# CLOCK +#--------------------------------------------- +time1_format = %H:%M +time1_font = sans bold 8 +time2_format = %A %d %B +time2_font = sans 7 +clock_font_color = #ececec 50 +clock_padding = 4 0 +clock_background_id = 1 + +#--------------------------------------------- +# MOUSE ACTION ON TASK +#--------------------------------------------- +mouse_middle = none +mouse_right = close +mouse_scroll_up = toggle +mouse_scroll_down = iconify + diff --git a/tintrc06 b/tintrc06 new file mode 100644 index 0000000..741bb8b --- /dev/null +++ b/tintrc06 @@ -0,0 +1,76 @@ +#--------------------------------------------- +# TINT CONFIG FILE +#--------------------------------------------- + +#--------------------------------------------- +# BACKGROUND AND BORDER +#--------------------------------------------- +rounded = 10 +border_width = 1 +background_color = #000000 45 +border_color = #ffffff 0 + +rounded = 7 +border_width = 1 +background_color = #ffffff 0 +border_color = #ffffff 70 + + +#--------------------------------------------- +# PANEL +#--------------------------------------------- +panel_monitor = 1 +panel_position = bottom left +panel_size = 1010 30 +panel_margin = 0 0 +panel_padding = 11 2 +font_shadow = 0 +panel_background_id = 0 + +#--------------------------------------------- +# TASKBAR +#--------------------------------------------- +taskbar_mode = multi_desktop +taskbar_padding = 3 3 +taskbar_background_id = 1 + +#--------------------------------------------- +# TASKS +#--------------------------------------------- +task_icon = 0 +task_text = 1 +task_width = 160 +task_centered = 1 +task_padding = 2 2 +task_font = sans bold 8 +task_font_color = #ffffff 60 +task_active_font_color = #ffffff 95 +task_background_id = 0 +task_active_background_id = 2 + +#--------------------------------------------- +# SYSTRAY +#--------------------------------------------- +#systray_padding = 9 3 +#systray_icon_opacity = 50 +#systray_background_id = 1 + +#--------------------------------------------- +# CLOCK +#--------------------------------------------- +time1_format = %A %d %H:%M +time1_font = sans bold 8 +#time2_format = %A %d %B +time2_font = sans 7 +clock_font_color = #ffffff 59 +clock_padding = 6 0 +clock_background_id = 1 + +#--------------------------------------------- +# MOUSE ACTION ON TASK +#--------------------------------------------- +mouse_middle = none +mouse_right = close +mouse_scroll_up = toggle +mouse_scroll_down = iconify +