Browse Source

Using the multiplatform branch as the new trunk

git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/slim/trunk@88 7c53e7cc-98ea-0310-8f1f-a0b24da60408
sip 18 years ago
commit
ef2bfe827a
35 changed files with 4768 additions and 0 deletions
  1. 339 0
      COPYING
  2. 43 0
      ChangeLog
  3. 23 0
      INSTALL
  4. 61 0
      Makefile
  5. 65 0
      Makefile.freebsd
  6. 65 0
      Makefile.netbsd
  7. 85 0
      Makefile.openbsd
  8. 58 0
      README
  9. 117 0
      THEMES
  10. 22 0
      TODO
  11. 784 0
      app.cpp
  12. 93 0
      app.h
  13. 242 0
      cfg.cpp
  14. 50 0
      cfg.h
  15. 52 0
      const.h
  16. 664 0
      image.cpp
  17. 73 0
      image.h
  18. 249 0
      input.cpp
  19. 63 0
      input.h
  20. 93 0
      jpeg.c
  21. 22 0
      main.cpp
  22. 550 0
      panel.cpp
  23. 130 0
      panel.h
  24. 161 0
      png.c
  25. 54 0
      slim.1
  26. 51 0
      slim.conf
  27. 101 0
      switchuser.cpp
  28. 47 0
      switchuser.h
  29. 2 0
      themes/default/COPYRIGHT.background
  30. 16 0
      themes/default/COPYRIGHT.panel
  31. 340 0
      themes/default/LICENSE.panel
  32. BIN
      themes/default/background.jpg
  33. BIN
      themes/default/panel.png
  34. 37 0
      themes/default/slim.theme
  35. 16 0
      xinitrc.sample

+ 339 - 0
COPYING

@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, 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
+
+	Appendix: 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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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.
+
+  <signature of Ty Coon>, 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.

+ 43 - 0
ChangeLog

@@ -0,0 +1,43 @@
+1.2.3 - 2005.09.11
+    * Added FreeBSD, NetBSD, OpenBSD support
+    * Replaced autotools with plain makefile(s)
+    * Added 'suspend' command (untested, we don't use it)
+    * add %theme variable in login_cmd
+    * Added support for %theme variable in login command
+	
+1.2.2 - 2005.05.21
+    * fix panel drawing on screens <= 1024x768
+    * Don't start X server unless valid theme found
+    * revert to 'default' of invalid theme specified
+    * try all themes from a set if one doesn't work
+    
+1.2.1 - 2005.05.17
+    * draw input directly on panel
+
+1.2.0 - 2005.05.16
+    * added theme preview (slim -p /path/to/theme)
+    * added JPEG support for panel image
+    * added 'center' background type and 'background_color' option
+    * added text shadow
+    * added warning when execution of login command fails
+    * Fix login failure when no shell specified in /etc/passwd
+    * Print error when login command execution fails
+    * add XNEST_DEBUG ifdef's to allow for easy debugging
+    * Add support for Ctrl-u and Ctrl-w
+    * Add 'vt07' to server arguments if not already specified
+    * Removes daemon option from the config file. Use slim -d
+    * Allow 'current_theme' to be a set of themes, choose randomly
+    * Change default theme
+
+1.1.0 - 2004.12.09
+    * error messages for X11 apps are no longer redirected
+      to the log file
+    * fixed text position for default theme
+    * added configurable shutdown and reboot messages
+    * separated 'Enter username' and 'Enter password' messages
+      position.
+    * due to the previous two points, the theme format has 
+      slightly changed
+    
+1.0.0 - 2004.12.07
+    * First public SLiM release

+ 23 - 0
INSTALL

@@ -0,0 +1,23 @@
+INSTALL file for SLiM
+
+0. Prerequisites:
+ - X.org or XFree86
+ - libpng
+ - libjpeg
+
+1. to build and install the program:
+ - edit the Makefile to adjust libraries and paths to your OS.
+ - make
+ - make install
+ 
+Pre-configured makefiles for some platform are available,
+i.e. for FreeBSD use:
+	- make -f Makefile.freebsd
+	- make -f Makefile.freebsd install
+
+2. automatic startup
+Edit the init scripts according to your OS/Distribution.
+
+
+
+

+ 61 - 0
Makefile

@@ -0,0 +1,61 @@
+# Makefile for slim - generic Linux
+#
+# Edit the following section to adjust the options
+# to fit into your operating system / distribution
+#######################################################
+CXX=/usr/bin/g++
+CC=/usr/bin/gcc
+CFLAGS=-I. -I/usr/X11R6/include -I/usr/include/freetype2 -I/usr/include/freetype2/config -I/usr/include/libpng12 -I/usr/include
+CXXLAGS=$(CFLAGS)
+LDFLAGS=-L/usr/X11R6/lib -lXft -lX11 -lfreetype -lXrender -lfontconfig -lpng12 -lz -lm -lcrypt -lXpm -lXmu -lpng -ljpeg
+CUSTOM=-DHAVE_SHADOW
+PREFIX=/usr
+CFGDIR=/etc
+MANDIR=/usr/man
+DESTDIR=
+#######################################################
+
+NAME=slim
+VERSION=1.2.3
+
+DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
+		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
+
+OBJECTS=jpeg.o png.o main.o image.o cfg.o switchuser.o input.o app.o panel.o
+
+all: slim
+
+slim: $(OBJECTS)
+	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
+
+.cpp.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+
+.c.o:
+	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+
+install: slim install-theme
+	install -D -m 755 slim $(DESTDIR)$(PREFIX)/bin/slim
+	install -D -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1/slim.1
+	install -D -m 644 slim.conf $(DESTDIR)$(CFGDIR)/slim.conf
+
+clean:
+	@rm -f slim *.o
+
+dist:
+	@rm -rf $(NAME)-$(VERSION)
+	@mkdir $(NAME)-$(VERSION)
+	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog README TODO \
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
+	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)
+
+
+install-theme:
+	install -D -m 644 themes/default/slim.theme \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/slim.theme
+	install -D -m 644 themes/default/panel.png \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/panel.png
+	install -D -m 644 themes/default/background.jpg \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/background.jpg

+ 65 - 0
Makefile.freebsd

@@ -0,0 +1,65 @@
+# Makefile for slim - FreeBSD 4 and 5
+#
+# Edit the following section to adjust the options
+# to fit into your operating system / distribution
+#######################################################
+CXX=/usr/bin/g++
+CC=/usr/bin/gcc
+CFLAGS=-I. -I/usr/X11R6/include -I/usr/local/include/freetype2 -I/usr/local/include/freetype2/config -I/usr/local/include/libpng -I/usr/local/include -I/usr/include
+CXXLAGS=$(CFLAGS)
+LDFLAGS=-L/usr/X11R6/lib -L/usr/local/lib -lXft -lX11 -lfreetype -lXrender -lfontconfig -lpng -lz -lm -lcrypt -lXpm -lXmu -lpng -ljpeg
+CUSTOM=-DNEEDS_BASENAME
+PREFIX=/usr
+CFGDIR=/etc
+MANDIR=/usr/share/man
+DESTDIR=
+#######################################################
+
+NAME=slim
+VERSION=1.2.3
+
+DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
+		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
+
+OBJECTS=jpeg.o png.o main.o image.o cfg.o switchuser.o input.o app.o panel.o
+
+all: slim
+
+slim: $(OBJECTS) 
+	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
+
+.cpp.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+
+.c.o:
+	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+
+install: slim install-theme
+	install -d $(DESTDIR)$(PREFIX)/bin
+	install -d $(DESTDIR)$(MANDIR)/man1
+	install -d $(DESTDIR)$(CFGDIR)
+	install -m 755 slim $(DESTDIR)$(PREFIX)/bin
+	install -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1
+	install -m 644 slim.conf $(DESTDIR)$(CFGDIR)
+
+clean:
+	@rm -f slim *.o
+
+dist:
+	@rm -rf $(NAME)-$(VERSION)
+	@mkdir $(NAME)-$(VERSION)
+	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog README TODO \
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
+	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)
+
+install-theme:
+	install -d -m 755 themes/default \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default
+	install -m 644 themes/default/slim.theme \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/slim.theme
+	install -m 644 themes/default/panel.png \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/panel.png
+	install -m 644 themes/default/background.jpg \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/background.jpg

+ 65 - 0
Makefile.netbsd

@@ -0,0 +1,65 @@
+# Makefile for slim - NetBSD
+#
+# Edit the following section to adjust the options
+# to fit into your operating system / distribution
+#######################################################
+CXX=/usr/bin/g++
+CC=/usr/bin/gcc
+CFLAGS=-I. -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -I/usr/X11R6/include/freetype2/config -I/usr/pkg/include -I/usr/include
+CXXLAGS=$(CFLAGS)
+LDFLAGS=-L/usr/X11R6/lib -L/usr/pkg/lib -lXft -lX11 -lfreetype -lXrender -lfontconfig -lpng -lz -lm -lcrypt -lXpm -lXmu -lpng -ljpeg
+CUSTOM=-DNEEDS_BASENAME
+PREFIX=/usr
+CFGDIR=/etc
+MANDIR=/usr/share/man
+DESTDIR=
+#######################################################
+
+NAME=slim
+VERSION=1.2.3
+
+DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
+		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
+
+OBJECTS=jpeg.o png.o main.o image.o cfg.o switchuser.o input.o app.o panel.o
+
+all: slim
+
+slim: $(OBJECTS) 
+	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
+
+.cpp.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+
+.c.o:
+	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+
+install: slim install-theme
+	install -d $(DESTDIR)$(PREFIX)/bin
+	install -d $(DESTDIR)$(MANDIR)/man1
+	install -d $(DESTDIR)$(CFGDIR)
+	install -m 755 slim $(DESTDIR)$(PREFIX)/bin
+	install -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1
+	install -m 644 slim.conf $(DESTDIR)$(CFGDIR)
+
+clean:
+	@rm -f slim *.o
+
+dist:
+	@rm -rf $(NAME)-$(VERSION)
+	@mkdir $(NAME)-$(VERSION)
+	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog README TODO \
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
+	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)
+
+install-theme:
+	install -d -m 755 themes/default \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default
+	install -m 644 themes/default/slim.theme \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/slim.theme
+	install -m 644 themes/default/panel.png \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/panel.png
+	install -m 644 themes/default/background.jpg \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/background.jpg

+ 85 - 0
Makefile.openbsd

@@ -0,0 +1,85 @@
+# Makefile for slim - OpenBSD
+#
+# Edit the following section to adjust the options
+# to fit into your operating system / distribution
+#######################################################
+CXX=/usr/bin/g++
+CC=/usr/bin/gcc
+CFLAGS=-I. -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -I/usr/X11R6/include/freetype2/config -I/usr/local/include/libpng -I/usr/local/include -I/usr/include
+CXXLAGS=$(CFLAGS)
+LDFLAGS=-L/usr/X11R6/lib -L/usr/local/lib -lXft -lX11 -lfreetype -lXrender -lfontconfig -lpng -lz -lm -lXpm -lXmu -lpng -ljpeg
+CUSTOM=-DNEEDS_BASENAME
+PREFIX=/usr
+CFGDIR=/etc
+MANDIR=/usr/share/man
+DESTDIR=
+#######################################################
+
+NAME=slim
+VERSION=1.2.3
+
+DEFINES=-DPACKAGE=\"$(NAME)\" -DVERSION=\"$(VERSION)\" \
+		-DPKGDATADIR=\"$(PREFIX)/share/slim\" -DSYSCONFDIR=\"$(CFGDIR)\"
+
+OBJECTS=jpeg.o png.o main.o image.o cfg.o switchuser.o input.o app.o panel.o
+
+.SUFFIXES: .c.o .cpp.o
+
+all: slim
+
+slim: $(OBJECTS) 
+	$(CXX) $(LDFLAGS) $(OBJECTS) -o $(NAME)
+
+main.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c main.cpp -o $@
+
+image.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c image.cpp -o $@
+
+cfg.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c cfg.cpp -o $@
+
+switchuser.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c switchuser.cpp -o $@
+
+input.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c input.cpp -o $@
+
+app.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c app.cpp -o $@
+
+panel.o:
+	$(CXX) $(CFLAGS) $(DEFINES) $(CUSTOM) -c panel.cpp -o $@
+
+.c.o:
+	$(CC) $(CFLAGS) $(DEFINES) $(CUSTOM) -c $< -o $@
+
+install: slim install-theme
+	install -d $(DESTDIR)$(PREFIX)/bin
+	install -d $(DESTDIR)$(MANDIR)/man1
+	install -d $(DESTDIR)$(CFGDIR)
+	install -m 755 slim $(DESTDIR)$(PREFIX)/bin
+	install -m 644 slim.1 $(DESTDIR)$(MANDIR)/man1
+	install -m 644 slim.conf $(DESTDIR)$(CFGDIR)
+
+clean:
+	@rm -f slim *.o
+
+dist:
+	@rm -rf $(NAME)-$(VERSION)
+	@mkdir $(NAME)-$(VERSION)
+	@cp -r *.cpp *.h *.c Makefile Makefile.* COPYING ChangeLog README TODO \
+		xinitrc.sample slim.1 THEMES themes slim.conf $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)/themes/.svn	$(NAME)-$(VERSION)/themes/default/.svn
+	@tar cvzf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
+	@rm -rf $(NAME)-$(VERSION)
+
+install-theme:
+	install -d -m 755 themes/default \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default
+	install -m 644 themes/default/slim.theme \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/slim.theme
+	install -m 644 themes/default/panel.png \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/panel.png
+	install -m 644 themes/default/background.jpg \
+		 $(DESTDIR)$(PREFIX)/share/slim/themes/default/background.jpg

+ 58 - 0
README

@@ -0,0 +1,58 @@
+README file for SLiM
+
+Simone Rota <sip@varlock.com>
+
+INTRODUCTION
+    SLiM (Simple Login Manager) is a graphical login manager for X11.
+    It aims to be simple, fast and independent from the various
+    desktop environments.
+    SLiM is based on latest stable release of Login.app by Per Lidén.
+
+    New features:
+    - External themes and configuration
+    - PNG support with alpha transparency for panel
+    - PNG / JPEG support for backgrounds
+    - XFT / freetype support
+    - Double or single (GDM-style) inputbox support
+    - Automake-based build procedure
+
+INSTALLATION
+    see the INSTALL file
+    
+USAGE
+
+	To launch slim, execute run the slim binary,
+    followed by the -d option if you want it to
+    run as a daemon in the background (reccommended)
+    
+    enter username and password to login. The ~/.xinitrc
+    file is executed by default, so be sure to have a working
+    .xinitrc file in your home.
+    
+    Special usernames (commands configurable in the config file):
+    - console: start console login
+    - exit: exit SLiM
+    - halt: halt the system
+    - reboot: reboot the system
+    
+    pressing the F11 key executes a user-specified
+    command (see the configuration file), the default
+    is to take a screenshot if the 'import' program is available.
+    
+CONFIGURATION
+    /usr/etc/slim.conf is the main configuration file.
+    Options are explained in the file itself
+    
+THEMES
+    See THEMES
+
+COPYRIGHT
+    SLiM is copyright (c) 2004-05 by Simone Rota, Johannes Winkelmann
+    and is available under the GNU General Public License.
+    See the COPYING file for the complete license.
+
+    Image handling code adapted and extended from xplanet 1.0.1,
+    copyright (c) 2002-04 by Hari Nair
+
+    Login.app is copyright (c) 1997, 1998 by Per Liden and is 
+    licensed through the GNU General Public License. 

+ 117 - 0
THEMES

@@ -0,0 +1,117 @@
+Quick THEME howto for SLiM
+
+Some basic information regarding the slim theme format.
+Read this file if you plan to make some theme for
+the program, and of course have a look at the included themes
+
+GENERAL CONCEPT
+    A SLiM theme essentially consists of:
+    - a background image (background.png or background.jpg)
+    - a panel image (panel.png or panel.jpg)
+    - input box(es) and messages and their placement
+      and properties (slim.theme)
+    
+    The panel and background images can be a PNG or JPEG file.
+    The panel is blended into the background image,
+    taking care of alpha transparency.
+
+SUPPORTED FORMATS
+    - fonts: use the xft font specs, ie: Verdana:size=16:bold
+    - colors: use html hex format, ie #0066CC
+    - positions: can be either absolute in pixels, ie 350
+      or relative to the container, ie 50% is in the middle
+      of the screen.
+    
+OPTIONS
+    The following is an example slim.theme file    
+    ----------------------------------------------------------------------
+    # Color, font, position for the messages (ie: shutting down)
+    msg_color               #FFFFFF
+    msg_font                Verdana:size=16:bold
+    msg_x                   50%
+    msg_y                   30
+
+    # style of background: 'stretch', 'tile', 'center', 'color'
+    background_style        stretch
+    background_color		#FF0033
+
+    # Horizonatal and vertical position for the panel.
+    input_panel_x           50%
+    input_panel_y           40%
+    
+    # input controls horizontal and vertical positions.
+    # IMPORTANT! set input_pass_x and input_pass_y to -1
+    # to use a single input box for username/password (GDM Style).
+    # Note that this fields only accept absolute values.
+    input_name_x            40
+    input_name_y            100
+    input_pass_x            40
+    input_pass_y            120
+    
+    # Input controls font and color
+    input_font          	Verdana:size=12
+    input_color             #000000
+
+    # Welcome message position. (relative to the panel)
+    # use -1 for both values or comment the options to disable
+    # the welcome message
+    welcome_x              	50%
+    welcome_y              	38
+    
+    # Font and color for the welcome message
+    welcome_font          	Verdana:size=16:bold:slant=italic
+    welcome_color          	#d7dde8
+
+    # 'Enter username' font and foreground/background color
+    username_font          	Verdana:size=12
+    username_color          	#d7dde8
+
+    # 'Enter username' and 'Enter password' position (relative to the panel)
+    #  use -1 for both values to disable the message
+    # note that in case of single inputbox the password values are ignored.
+    username_x                 50%
+    username_y                 146
+    password_x                 50%
+    password_y                 146
+
+    # The message to be displayed. Leave blank if no message
+    # is needed (ie, when already present in the panel image)
+    username_msg            Please enter your username
+    password_msg            Please enter your password
+    ----------------------------------------------------------------------
+
+SHADOWS
+
+    The 'msg', 'input', 'welcome' and 'username' sections support
+    shadows; three values can be configured:
+    - color: the shadow color
+    - x offset: the offset in x direction, relative to the normal text 
+    - y offset: the offset in y direction, relative to the normal text
+
+    So to add a text shadow to the welcome message, add the following
+    to slim.conf:
+    ----------------------------------------------------------------------
+    welcome_shadow_xoffset      -2
+    welcome_shadow_yoffset      2
+    welcome_shadow_color        #ff0000
+    ----------------------------------------------------------------------
+     
+    The other keys are analogue:
+    ----------------------------------------------------------------------
+    # for username and password label
+    username_shadow_xoffset 	2
+    username_shadow_yoffset 	-2
+    username_shadow_color   	#ff0000
+
+    # for the input fields
+    input_shadow_xoffset 	1
+    input_shadow_yoffset 	1
+    input_shadow_color   	#0000ff
+
+    # for the messages:
+    msg_shadow_xoffset 		1    
+    msg_shadow_yoffset 		1    
+    msg_shadow_color   		#ff00ff
+    ----------------------------------------------------------------------
+
+    

+ 22 - 0
TODO

@@ -0,0 +1,22 @@
+1.2.2
+-----
+- drawing problem on screens <= 1024x768 (implemented)
+- Don't start X server if theme's not found
+
+1.2.x
+-----
+- i18n
+- don't choose a non-existing theme in random selection
+- think about multi-line configuration:
+    current_theme = t1, t2, t3
+    current_theme = t4, t5
+  or
+    current_theme = t1, t2, t3 \
+		    t4, t5
+- FreeBSD fixes
+
+2.0.x
+-----
+- restart X server on ctrl-alt-backspace
+- allow switching theme on the fly if a set is specified in slim.conf
+- text alignment, to allow to center texts of unknown length (e.g. %host)

+ 784 - 0
app.cpp

@@ -0,0 +1,784 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <cstring>
+#include <cstdio>
+
+#include <sstream>
+#include <vector>
+#include <algorithm>
+#include "app.h"
+#include "image.h"
+
+
+extern App* LoginApp;
+
+void CatchSignal(int sig) {
+    cerr << APPNAME << ": unexpected signal " << sig << endl;
+    LoginApp->StopServer();
+    LoginApp->RemoveLock();
+    exit(ERR_EXIT);
+}
+
+
+void AlarmSignal(int sig) {
+    int pid = LoginApp->GetServerPID();
+    if(waitpid(pid, NULL, WNOHANG) == pid) {
+        LoginApp->StopServer();
+        LoginApp->RemoveLock();
+        exit(OK_EXIT);
+    }
+    signal(sig, AlarmSignal);
+    alarm(2);
+}
+
+
+void User1Signal(int sig) {
+    signal(sig, User1Signal);
+}
+
+
+App::App(int argc, char** argv) {
+
+    int tmp;
+    ServerPID = -1;
+    testing = false;
+
+    // Parse command line
+    while((tmp = getopt(argc, argv, "vhp:d?")) != EOF) {
+        switch (tmp) {
+        case 'p':	// Test theme
+            testtheme = optarg;
+            testing = true;
+            if (testtheme == NULL) {
+                cerr << "The -p option requires an argument" << endl;
+                exit(ERR_EXIT);
+            }
+            break;
+        case 'd':	// Daemon mode
+            daemonmode = true;
+            break;
+        case 'v':	// Version
+            std::cout << APPNAME << " version " << VERSION << endl;
+            exit(OK_EXIT);
+            break;
+        case '?':	// Illegal
+            cerr << endl;
+        case 'h':   // Help
+            cerr << "usage:  " << APPNAME << " [option ...]" << endl
+            << "options:" << endl
+            << "    -d: daemon mode" << endl
+            << "    -v: show version" << endl
+            << "    -p /path/to/theme/dir: preview theme" << endl;
+            exit(OK_EXIT);
+            break;
+        }
+    }
+
+    if (getuid() != 0 && !testing) {
+        cerr << APPNAME << ": only root can run this program" << endl;
+        exit(ERR_EXIT);
+    }
+
+}
+
+
+void App::Run() {
+
+    DisplayName = DISPLAY;
+
+#ifdef XNEST_DEBUG
+    char* p = getenv("DISPLAY");
+    if (p && p[0]) {
+        DisplayName = p;
+        cout << "Using display name " << DisplayName << endl;
+    }
+#endif
+
+    // Read configuration and theme
+    cfg.readConf(CFGFILE);
+    string themebase = "";
+    string themefile = "";
+    string themedir = "";
+    themeName = "";
+    if (testing) {
+        themeName = testtheme;
+    } else {
+        themebase = string(THEMESDIR) + "/";
+        themeName = cfg.getOption("current_theme");
+        string::size_type pos;
+        if ((pos = themeName.find(",")) != string::npos) {
+            // input is a set
+            themeName = findValidRandomTheme(themeName);
+            if (themeName == "") {
+                themeName = "default";
+            }
+        }
+    }
+
+    bool loaded = false;
+    while (!loaded) {
+        themedir =  themebase + themeName;
+        themefile = themedir + THEMESFILE;
+        if (!cfg.readConf(themefile)) {
+            if (themeName == "default") {
+                cerr << APPNAME << ": Failed to open default theme file "
+                     << themefile << endl;
+                exit(ERR_EXIT);
+            } else {
+                cerr << APPNAME << ": Invalid theme in config: "
+                     << themeName << endl;
+                themeName = "default";
+            }
+        } else {
+            loaded = true;
+        }
+    }
+
+    if (!testing) {
+        // Create lock file
+        LoginApp->GetLock();
+
+        // Start x-server
+        setenv("DISPLAY", DisplayName, 1);
+        signal(SIGQUIT, CatchSignal);
+        signal(SIGTERM, CatchSignal);
+        signal(SIGKILL, CatchSignal);
+        signal(SIGINT, CatchSignal);
+        signal(SIGHUP, CatchSignal);
+        signal(SIGPIPE, CatchSignal);
+        signal(SIGUSR1, User1Signal);
+        signal(SIGALRM, AlarmSignal);
+
+#ifndef XNEST_DEBUG
+        OpenLog();
+
+        // Daemonize
+        if (daemonmode) {
+            if (daemon(0, 1) == -1) {
+                cerr << APPNAME << ": " << strerror(errno) << endl;
+                exit(ERR_EXIT);
+            }
+        }
+
+        StartServer();
+        alarm(2);
+#endif
+
+    }
+
+    // Open display
+    if((Dpy = XOpenDisplay(DisplayName)) == 0) {
+        cerr << APPNAME << ": could not open display '"
+             << DisplayName << "'" << endl;
+        if (!testing) StopServer();
+        exit(ERR_EXIT);
+    }
+
+    // Get screen and root window
+    Scr = DefaultScreen(Dpy);
+    Root = RootWindow(Dpy, Scr);
+
+
+    // for tests we use a standard window
+    if (testing) {
+        Window RealRoot = RootWindow(Dpy, Scr);
+        Root = XCreateSimpleWindow(Dpy, RealRoot, 0, 0, 640, 480, 0, 0, 0);
+        XMapWindow(Dpy, Root);
+        XFlush(Dpy);
+    } else {
+        blankScreen();
+    }
+
+    HideCursor();
+
+    // Create panel
+    LoginPanel = new Panel(Dpy, Scr, Root, &cfg, themedir);
+
+    // Start looping
+    XEvent event;
+    int panelclosed = 1;
+    int Action;
+    bool firstloop = true; // 1st time panel is shown (for automatic username)
+
+    while(1) {
+        if(panelclosed) {
+            // Init root
+            setBackground(themedir);
+
+            // Close all clients
+            if (!testing) {
+                KillAllClients(False);
+                KillAllClients(True);
+            }
+
+            // Show panel
+            LoginPanel->OpenPanel();
+        }
+
+        Action = WAIT;
+        LoginPanel->GetInput()->Reset();
+        if (firstloop && cfg.getOption("default_user") != "") {
+            LoginPanel->GetInput()->SetName(cfg.getOption("default_user") );
+            firstloop = false;
+        }
+
+        while(Action == WAIT) {
+            XNextEvent(Dpy, &event);
+            Action = LoginPanel->EventHandler(&event);
+        }
+
+        if(Action == FAIL) {
+            panelclosed = 0;
+            LoginPanel->ClearPanel();
+            XBell(Dpy, 100);
+        } else {
+            // for themes test we just quit
+            if (testing) {
+                Action = EXIT;
+            }
+            panelclosed = 1;
+            LoginPanel->ClosePanel();
+
+            switch(Action) {
+                case LOGIN:
+                    Login();
+                    break;
+                case CONSOLE:
+                    Console();
+                    break;
+                case REBOOT:
+                    Reboot();
+                    break;
+                case HALT:
+                    Halt();
+                    break;
+                case SUSPEND:
+                    Suspend();
+                    break;
+                case EXIT:
+                    Exit();
+                    break;
+            }
+        }
+    }
+}
+
+
+int App::GetServerPID() {
+    return ServerPID;
+}
+
+// Hide the cursor
+void App::HideCursor() {
+	XColor		    black;
+	char		    cursordata[1];
+	Pixmap		    cursorpixmap;
+	Cursor		    cursor;
+	cursordata[0]=0;
+	cursorpixmap=XCreateBitmapFromData(Dpy,Root,cursordata,1,1);
+	black.red=0;
+	black.green=0;
+	black.blue=0;
+	cursor=XCreatePixmapCursor(Dpy,cursorpixmap,cursorpixmap,&black,&black,0,0);
+	XDefineCursor(Dpy,Root,cursor);
+}
+
+void App::Login() {
+    struct passwd *pw;
+    pid_t pid;
+
+    pw = LoginPanel->GetInput()->GetPasswdStruct();
+    if(pw == 0)
+        return;
+
+    // Create new process
+    pid = fork();
+    if(pid == 0) {
+        // Login process starts here
+        SwitchUser Su(pw, &cfg, DisplayName);
+        string session = LoginPanel->getSession();
+	string loginCommand = cfg.getOption("login_cmd");
+        replaceVariables(loginCommand, SESSION_VAR, session);
+        replaceVariables(loginCommand, THEME_VAR, themeName);
+        Su.Login(loginCommand.c_str());
+        exit(OK_EXIT);
+    }
+
+#ifndef XNEST_DEBUG
+    CloseLog();
+#endif
+
+    // Wait until user is logging out (login process terminates)
+    pid_t wpid = -1;
+    int status;
+    while (wpid != pid) {
+        wpid = wait(&status);
+    }
+    if (WIFEXITED(status) && WEXITSTATUS(status)) {
+        LoginPanel->Message("Failed to execute login command");
+    }
+
+    // Close all clients
+    KillAllClients(False);
+    KillAllClients(True);
+
+    // Send HUP signal to clientgroup
+    killpg(pid, SIGHUP);
+
+    // Send TERM signal to clientgroup, if error send KILL
+    if(killpg(pid, SIGTERM))
+    killpg(pid, SIGKILL);
+
+    HideCursor();
+
+#ifndef XNEST_DEBUG
+    // Re-activate log file
+    OpenLog();
+#endif
+}
+
+
+void App::Reboot() {
+    // Stop alarm clock
+    alarm(0);
+
+    // Write message
+    LoginPanel->Message((char*)cfg.getOption("reboot_msg").c_str());
+    sleep(3);
+
+    // Stop server and reboot
+    StopServer();
+    RemoveLock();
+    system(cfg.getOption("reboot_cmd").c_str());
+    exit(OK_EXIT);
+}
+
+
+void App::Halt() {
+    // Stop alarm clock
+    alarm(0);
+
+    // Write message
+    LoginPanel->Message((char*)cfg.getOption("shutdown_msg").c_str());
+    sleep(3);
+
+    // Stop server and halt
+    StopServer();
+    RemoveLock();
+    system(cfg.getOption("halt_cmd").c_str());
+    exit(OK_EXIT);
+}
+
+void App::Suspend() {
+    sleep(1);
+    system(cfg.getOption("suspend_cmd").c_str());
+}
+
+
+void App::Console() {
+    int posx = 40;
+    int posy = 40;
+    int fontx = 9;
+    int fonty = 15;
+    int width = (XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posx * 2)) / fontx;
+    int height = (XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posy * 2)) / fonty;
+
+    // Execute console
+    const char* cmd = cfg.getOption("console_cmd").c_str();
+    char *tmp = new char[strlen(cmd) + 60];
+    sprintf(tmp, cmd, width, height, posx, posy, fontx, fonty);
+    system(tmp);
+    delete [] tmp;
+}
+
+
+void App::Exit() {
+    if (testing) {
+        char* testmsg = "This is a test message :-)";
+        LoginPanel->Message(testmsg);
+        sleep(3);
+    } else {
+        delete LoginPanel;
+        StopServer();
+        RemoveLock();
+    }
+    exit(OK_EXIT);
+}
+
+
+int CatchErrors(Display *dpy, XErrorEvent *ev) {
+    return 0;
+}
+
+
+void App::KillAllClients(Bool top) {
+    Window dummywindow;
+    Window *children;
+    unsigned int nchildren;
+    unsigned int i;
+    XWindowAttributes attr;
+
+    XSync(Dpy, 0);
+    XSetErrorHandler(CatchErrors);
+
+    nchildren = 0;
+    XQueryTree(Dpy, Root, &dummywindow, &dummywindow, &children, &nchildren);
+    if(!top) {
+        for(i=0; i<nchildren; i++) {
+            if(XGetWindowAttributes(Dpy, children[i], &attr) && (attr.map_state == IsViewable))
+                children[i] = XmuClientWindow(Dpy, children[i]);
+            else
+                children[i] = 0;
+        }
+    }
+
+    for(i=0; i<nchildren; i++) {
+        if(children[i])
+            XKillClient(Dpy, children[i]);
+    }
+    XFree((char *)children);
+
+    XSync(Dpy, 0);
+    XSetErrorHandler(NULL);
+}
+
+
+int App::ServerTimeout(int timeout, char* text) {
+    int	i = 0;
+    int pidfound = -1;
+    static char	*lasttext;
+
+    for(;;) {
+        pidfound = waitpid(ServerPID, NULL, WNOHANG);
+        if(pidfound == ServerPID)
+            break;
+        if(timeout) {
+            if(i == 0 && text != lasttext)
+                cerr << endl << APPNAME << ": waiting for " << text;
+            else
+                cerr << ".";
+        }
+        if(timeout)
+            sleep(1);
+        if(++i > timeout)
+            break;
+    }
+
+    if(i > 0)
+        cerr << endl;
+    lasttext = text;
+
+    return (ServerPID != pidfound);
+}
+
+
+int App::WaitForServer() {
+    int	ncycles	 = 120;
+    int	cycles;
+
+    for(cycles = 0; cycles < ncycles; cycles++) {
+        if((Dpy = XOpenDisplay(DisplayName))) {
+            return 1;
+        } else {
+            if(!ServerTimeout(1, "X server to begin accepting connections"))
+                break;
+        }
+    }
+
+    cerr << "Giving up." << endl;
+
+    return 0;
+}
+
+
+int App::StartServer() {
+    ServerPID = vfork();
+
+    static const int MAX_XSERVER_ARGS = 256;
+    static char* server[MAX_XSERVER_ARGS+2] = { NULL };
+    server[0] = (char *)cfg.getOption("default_xserver").c_str();
+    string argOption = cfg.getOption("xserver_arguments");
+    char* args = new char[argOption.length()+2]; // NULL plus vt
+    strcpy(args, argOption.c_str());
+
+    int argc = 1;
+    int pos = 0;
+    bool hasVtSet = false;
+    while (args[pos] != '\0') {
+        if (args[pos] == ' ' || args[pos] == '\t') {
+            *(args+pos) = '\0';
+            server[argc++] = args+pos+1;
+        } else if (pos == 0) {
+            server[argc++] = args+pos;
+        }
+        if (server[argc-1][0] == 'v' && server[argc-1][1] == 't') {
+            bool ok = false;
+            Cfg::string2int(server[argc-1]+2, &ok);
+            if (ok) {
+                hasVtSet = true;
+            }
+        }
+        ++pos;
+
+        if (argc+1 >= MAX_XSERVER_ARGS) {
+            // ignore _all_ arguments to make sure the server starts at
+            // all
+            argc = 1;
+            break;
+        }
+    }
+    if (!hasVtSet && daemonmode) {
+        server[argc++] = "vt07";
+    }
+    server[argc] = NULL;
+
+    switch(ServerPID) {
+    case 0:
+        signal(SIGTTIN, SIG_IGN);
+        signal(SIGTTOU, SIG_IGN);
+        signal(SIGUSR1, SIG_IGN);
+        setpgid(0,getpid());
+
+
+        execvp(server[0], server);
+        cerr << APPNAME << ": X server could not be started" << endl;
+        exit(ERR_EXIT);
+        break;
+
+    case -1:
+        break;
+
+    default:
+        errno = 0;
+        if(!ServerTimeout(0, "")) {
+            ServerPID = -1;
+            break;
+        }
+        alarm(15);
+        pause();
+        alarm(0);
+
+        // Wait for server to start up
+        if(WaitForServer() == 0) {
+            cerr << APPNAME << ": unable to connect to X server" << endl;
+            StopServer();
+            ServerPID = -1;
+            exit(ERR_EXIT);
+        }
+        break;
+    }
+
+    delete args;
+
+    return ServerPID;
+}
+
+
+jmp_buf CloseEnv;
+int IgnoreXIO(Display *d) {
+    cerr << APPNAME << ": connection to X server lost." << endl;
+    longjmp(CloseEnv, 1);
+}
+
+
+void App::StopServer() {
+    // Stop alars clock and ignore signals
+    alarm(0);
+    signal(SIGQUIT, SIG_IGN);
+    signal(SIGINT, SIG_IGN);
+    signal(SIGHUP, SIG_IGN);
+    signal(SIGPIPE, SIG_IGN);
+    signal(SIGTERM, SIG_DFL);
+    signal(SIGKILL, SIG_DFL);
+    signal(SIGALRM, SIG_DFL);
+
+    // Catch X error
+    XSetIOErrorHandler(IgnoreXIO);
+    if(!setjmp(CloseEnv))
+        XCloseDisplay(Dpy);
+
+    // Send HUP to process group
+    errno = 0;
+    if((killpg(getpid(), SIGHUP) != 0) && (errno != ESRCH))
+        cerr << APPNAME << ": can't send HUP to process group " << getpid() << endl;
+
+    // Send TERM to server
+    if(ServerPID < 0)
+        return;
+    errno = 0;
+    if(killpg(ServerPID, SIGTERM) < 0) {
+        if(errno == EPERM) {
+            cerr << APPNAME << ": can't kill X server" << endl;
+            exit(ERR_EXIT);
+        }
+        if(errno == ESRCH)
+            return;
+    }
+
+    // Wait for server to shut down
+    if(!ServerTimeout(10, "X server to shut down")) {
+        cerr << endl;
+        return;
+    }
+
+    cerr << endl << APPNAME << ":  X server slow to shut down, sending KILL signal." << endl;
+
+    // Send KILL to server
+    errno = 0;
+    if(killpg(ServerPID, SIGKILL) < 0) {
+        if(errno == ESRCH)
+            return;
+    }
+
+    // Wait for server to die
+    if(ServerTimeout(3, "server to die")) {
+        cerr << endl << APPNAME << ": can't kill server" << endl;
+        exit(ERR_EXIT);
+    }
+    cerr << endl;
+}
+
+
+void App::blankScreen()
+{
+    GC gc = XCreateGC(Dpy, Root, 0, 0);
+    XSetForeground(Dpy, gc, BlackPixel(Dpy, Scr));
+    XFillRectangle(Dpy, Root, gc, 0, 0,
+                   XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)),
+                   XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
+    XFlush(Dpy);
+    XFreeGC(Dpy, gc);
+
+}
+
+void App::setBackground(const string& themedir) {
+    string filename;
+    filename = themedir + "/background.png";
+    Image *image = new Image;
+    bool loaded = image->Read(filename.c_str());
+    if (!loaded){ // try jpeg if png failed
+        filename = "";
+        filename = themedir + "/background.jpg";
+        loaded = image->Read(filename.c_str());
+    }
+    if (loaded) {
+        string bgstyle = cfg.getOption("background_style");
+        if (bgstyle == "stretch") {
+            image->Resize(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
+        } else if (bgstyle == "tile") {
+            image->Tile(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
+        } else if (bgstyle == "center") {
+    	    string hexvalue = cfg.getOption("background_color");
+            hexvalue = hexvalue.substr(1,6);
+    	    image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+        			    hexvalue.c_str());
+        } else { // plain color or error
+    	    string hexvalue = cfg.getOption("background_color");
+            hexvalue = hexvalue.substr(1,6);
+    	    image->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+        			    hexvalue.c_str());
+        }
+        Pixmap p = image->createPixmap(Dpy, Scr, Root);
+        XSetWindowBackgroundPixmap(Dpy, Root, p);
+    }
+    XClearWindow(Dpy, Root);
+
+    XFlush(Dpy);
+}
+
+// Lock or die!
+void App::GetLock() {
+    int fd;
+    fd=open(cfg.getOption("lockfile").c_str(),O_WRONLY | O_CREAT | O_EXCL);
+    if (fd<0 && errno==EEXIST) {
+        cerr << APPNAME << ": It appears there is another instance of the program already running" <<endl
+            << "If not, try to remove the lockfile: " << cfg.getOption("lockfile") <<endl;
+        exit(ERR_EXIT);
+    } else if (fd < 0) {
+        cerr << APPNAME << ": Could not accesss lock file: " << cfg.getOption("lockfile") << endl;
+        exit(ERR_EXIT);
+    }
+}
+
+// Remove lockfile and close logs
+void App::RemoveLock() {
+    remove(cfg.getOption("lockfile").c_str());
+}
+
+// Redirect stdout and stderr to log file
+void App::OpenLog() {
+    FILE *log = fopen (cfg.getOption("logfile").c_str(),"a");
+    if (!log) {
+        cerr <<  APPNAME << ": Could not accesss log file: " << cfg.getOption("logfile") << endl;
+        RemoveLock();
+        exit(ERR_EXIT);
+    }
+    fclose(log);
+    freopen (cfg.getOption("logfile").c_str(),"a",stdout);
+    setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+    freopen (cfg.getOption("logfile").c_str(),"a",stderr);
+    setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+}
+
+
+// Relases stdout/err
+void App::CloseLog(){
+    fclose(stderr);
+    fclose(stdout);
+}
+
+string App::findValidRandomTheme(const string& set)
+{
+    // extract random theme from theme set; return empty string on error
+    string name = set;
+    struct stat buf;
+
+    if (name[name.length()-1] == ',') {
+        name = name.substr(0, name.length() - 1);
+    }
+
+    srandom(getpid()+time(NULL));
+
+    vector<string> themes;
+    string themefile;
+    Cfg::split(themes, name, ',');
+    do {
+        int sel = random() % themes.size();
+
+        name = Cfg::Trim(themes[sel]);
+        themefile = string(THEMESDIR) +"/" + name + THEMESFILE;
+        if (stat(themefile.c_str(), &buf) != 0) {
+            themes.erase(find(themes.begin(), themes.end(), name));
+            cerr << APPNAME << ": Invalid theme in config: "
+                 << name << endl;
+            name = "";
+        }
+    } while (name == "" && themes.size());
+    return name;
+}
+
+
+void App::replaceVariables(string& input,
+			   const string& var,
+			   const string& value)
+{
+    string::size_type pos = 0;
+    int len = var.size();
+    while ((pos = input.find(var, pos)) != string::npos) {
+        input = input.substr(0, pos) + value + input.substr(pos+len);
+    }
+}

+ 93 - 0
app.h

@@ -0,0 +1,93 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#ifndef _APP_H_
+#define _APP_H_
+
+#include <X11/Xlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <iostream>
+#include "panel.h"
+#include "cfg.h"
+
+using namespace std;
+
+class App {
+public:
+    App(int argc, char** argv);
+    ~App();
+    void Run();
+    int GetServerPID();
+    void StopServer();
+
+    // Lock functions
+    void GetLock();
+    void RemoveLock();
+
+private:
+    void Login();
+    void Reboot();
+    void Halt();
+    void Suspend();
+    void Console();
+    void Exit();
+    void KillAllClients(Bool top);
+    void ReadConfig();
+    void OpenLog();
+    void CloseLog();
+    void HideCursor();
+
+    static std::string findValidRandomTheme(const std::string& set);
+    static void App::replaceVariables(std::string& input,
+				      const std::string& var,
+				      const std::string& value);
+
+    // Server functions
+    int StartServer();
+    int ServerTimeout(int timeout, char *string);
+    int WaitForServer();
+
+    // Private data
+    Window Root;
+    Display* Dpy;
+    int Scr;
+    Panel* LoginPanel;
+    int ServerPID;
+    char* DisplayName;
+
+    // Options
+    char* DispName;
+
+    Cfg cfg;
+
+    XpmAttributes BackgroundPixmapAttributes;
+    Pixmap BackgroundPixmap;
+
+    void App::blankScreen();
+    void setBackground(const string& themedir);
+	
+    bool daemonmode;
+	// For testing themes
+	char* testtheme;
+    bool testing;
+    
+    std::string themeName;
+
+};
+
+
+#endif
+

+ 242 - 0
cfg.cpp

@@ -0,0 +1,242 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#include <fstream>
+#include <string>
+#include <iostream>
+#include <unistd.h>
+
+#include "cfg.h"
+
+using namespace std;
+
+typedef pair<string,string> option;
+
+Cfg::Cfg() {
+    // Configuration options
+    options.insert(option("default_path","./:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin"));
+    options.insert(option("default_xserver","/usr/X11R6/bin/X"));
+    options.insert(option("xserver_arguments",""));
+    options.insert(option("login_cmd","exec /bin/bash -login ~/.xinitrc %session"));
+    options.insert(option("halt_cmd","/sbin/shutdown -h now"));
+    options.insert(option("reboot_cmd","/sbin/shutdown -r now"));
+    options.insert(option("suspend_cmd",""));
+    options.insert(option("console_cmd","/usr/X11R6/bin/xterm -C -fg white -bg black +sb -g %dx%d+%d+%d -fn %dx%d -T ""Console login"" -e /bin/sh -c ""/bin/cat /etc/issue; exec /bin/login"""));
+    options.insert(option("screenshot_cmd","import -window root /login.app.png"));
+    options.insert(option("welcome_msg","Welcome to %host"));
+    options.insert(option("default_user",""));
+    options.insert(option("current_theme","default"));
+    options.insert(option("lockfile","/var/run/slim.lock"));
+    options.insert(option("logfile","/var/log/slim.log"));
+    options.insert(option("shutdown_msg","The system is halting..."));
+    options.insert(option("reboot_msg","The system is rebooting..."));
+    options.insert(option("sessions","wmaker,blackbox,icewm"));
+
+    // Theme stuff
+    options.insert(option("input_panel_x","50%"));
+    options.insert(option("input_panel_y","40%"));
+    options.insert(option("input_name_x","200"));
+    options.insert(option("input_name_y","154"));
+    options.insert(option("input_pass_x","-1")); // default is single inputbox
+    options.insert(option("input_pass_y","-1"));
+    options.insert(option("input_font","Verdana:size=11"));
+    options.insert(option("input_color", "#000000"));
+    options.insert(option("input_cursor_height","20"));
+    options.insert(option("input_maxlength_name","20"));
+    options.insert(option("input_maxlength_passwd","20"));
+    options.insert(option("input_shadow_xoffset", "0"));
+    options.insert(option("input_shadow_yoffset", "0"));
+    options.insert(option("input_shadow_color","#FFFFFF"));
+
+    options.insert(option("welcome_font","Verdana:size=14"));
+    options.insert(option("welcome_color","#FFFFFF"));
+    options.insert(option("welcome_x","-1"));
+    options.insert(option("welcome_y","-1"));
+    options.insert(option("welcome_shadow_xoffset", "0"));
+    options.insert(option("welcome_shadow_yoffset", "0"));
+    options.insert(option("welcome_shadow_color","#FFFFFF"));
+
+    options.insert(option("intro_msg",""));
+    options.insert(option("intro_font","Verdana:size=14"));
+    options.insert(option("intro_color","#FFFFFF"));
+    options.insert(option("intro_x","-1"));
+    options.insert(option("intro_y","-1"));
+
+    options.insert(option("background_style","stretch"));
+    options.insert(option("background_color","#CCCCCC"));
+
+    options.insert(option("username_font","Verdana:size=12"));
+    options.insert(option("username_color","#FFFFFF"));
+    options.insert(option("username_x","-1"));
+    options.insert(option("username_y","-1"));
+    options.insert(option("username_msg","Please enter your username"));
+    options.insert(option("username_shadow_xoffset", "0"));
+    options.insert(option("username_shadow_yoffset", "0"));
+    options.insert(option("username_shadow_color","#FFFFFF"));
+
+    options.insert(option("password_x","-1"));
+    options.insert(option("password_y","-1"));
+    options.insert(option("password_msg","Please enter your password"));
+
+    options.insert(option("msg_color","#FFFFFF"));
+    options.insert(option("msg_font","Verdana:size=16:bold"));
+    options.insert(option("msg_x","40"));
+    options.insert(option("msg_y","40"));
+    options.insert(option("msg_shadow_xoffset", "0"));
+    options.insert(option("msg_shadow_yoffset", "0"));
+    options.insert(option("msg_shadow_color","#FFFFFF"));
+
+    error = "";
+
+}
+
+/*
+ * Creates the Cfg object and parses
+ * known options from the given configfile / themefile
+ */
+bool Cfg::readConf(string configfile) {
+    int n = -1;
+    string line, fn(configfile);
+    map<string,string>::iterator it;
+    string op;
+    ifstream cfgfile( fn.c_str() );
+    if (cfgfile) {
+        while (getline( cfgfile, line )) {
+            it = options.begin();
+            while (it != options.end()) {
+                op = it->first;
+                n = line.find(op);
+                if (n == 0)
+                    options[op] = parseOption(line, op);
+                it++;
+            }
+        }
+        cfgfile.close();
+        return true;
+    } else {
+        error = "Cannot read configuration file: " + configfile;
+        return false;
+    }
+}
+
+/* Returns the option value, trimmed */
+string Cfg::parseOption(string line, string option ) {
+    return Trim( line.substr(option.size(), line.size() - option.size()));
+}
+
+
+const string& Cfg::getError() const {
+    return error;
+}
+
+string& Cfg::getOption(string option) {
+    return options[option];
+}
+
+/* return a trimmed string */
+string Cfg::Trim( const string& s ) {
+    if ( s.empty() ) {
+        return s;
+    }
+    int pos = 0;
+    string line = s;
+    string::size_type len = line.length();
+    while ( pos < len && isspace( line[pos] ) ) {
+        ++pos;
+    }
+    line.erase( 0, pos );
+    pos = line.length()-1;
+    while ( pos > -1 && isspace( line[pos] ) ) {
+        --pos;
+    }
+    if ( pos != -1 ) {
+        line.erase( pos+1 );
+    }
+    return line;
+}
+
+/* Return the welcome message with replaced vars */
+string Cfg::getWelcomeMessage(){
+    string s = getOption("welcome_msg");
+    int n = -1;
+    n = s.find("%host");
+    if (n >= 0) {
+        string tmp = s.substr(0, n);
+        char host[40];
+        gethostname(host,40);
+        tmp = tmp + host;
+        tmp = tmp + s.substr(n+5, s.size() - n);
+        s = tmp;
+    }
+    n = s.find("%domain");
+    if (n >= 0) {
+        string tmp = s.substr(0, n);;
+        char domain[40];
+        getdomainname(domain,40);
+        tmp = tmp + domain;
+        tmp = tmp + s.substr(n+7, s.size() - n);
+        s = tmp;
+    }
+    return s;
+}
+
+int Cfg::string2int(const char* string, bool* ok) {
+    char* err = 0;
+    int l = (int)strtol(string, &err, 10);
+    if (ok) {
+        *ok = (*err == 0);
+    }
+    return (*err == 0) ? l : 0;
+}
+
+// Get absolute position
+int Cfg::absolutepos(const string& position, int max, int width) {
+    int n = -1;
+    n = position.find("%");
+    if (n>0) { // X Position expressed in percentage
+        const char* tmp =  position.substr(0, n).c_str();
+        int result = (max*string2int(tmp)/100) - (width / 2);
+        return result < 0 ? 0 : result ;
+    } else { // Absolute X position
+        return string2int(position.c_str());
+    }
+}
+
+// split a comma separated string into a vector of strings
+void Cfg::split(vector<string>& v, const string& str, char c) {
+    v.clear();
+    string::const_iterator s = str.begin();
+    while (true) {
+        string::const_iterator begin = s;
+        while (*s != c && s != str.end()) { ++s; }
+        v.push_back(string(begin, s));
+        if (s == str.end()) {
+            break;
+        }
+        if (++s == str.end()) {
+            v.push_back("");
+            break;
+        }
+    }
+}
+
+string Cfg::nextSession(string current) {
+    vector<string> sessions;
+    split(sessions, getOption("sessions"), ',');
+    if (sessions.size() <= 1)
+        return current;
+
+    for (int i=0; i<sessions.size()-1; i++) {
+        if (current == sessions[i]) {
+            return sessions[i+1];
+        }
+    }
+    return sessions[0];
+}

+ 50 - 0
cfg.h

@@ -0,0 +1,50 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#ifndef _CFG_H_
+#define _CFG_H_
+
+#include <string>
+#include <map>
+#include <vector>
+
+#define INPUT_MAXLENGTH_NAME    30
+#define INPUT_MAXLENGTH_PASSWD  50
+
+#define CFGFILE SYSCONFDIR"/slim.conf"
+#define THEMESDIR PKGDATADIR"/themes"
+#define THEMESFILE "/slim.theme"
+
+using namespace std;
+
+class Cfg {
+
+public:
+    Cfg();
+    bool readConf(string configfile);
+    string parseOption(string line, string option);
+    const string& getError() const;
+    string& getOption(string option);
+    string getWelcomeMessage();
+
+    static int absolutepos(const string& position, int max, int width);
+    static int string2int(const char* string, bool* ok = 0);
+    static void split(vector<string>& v, const string& str, char c);
+    static string Trim(const string& s);
+
+    string nextSession(string current);
+
+private:
+    map<string,string> options;
+    string error;
+
+};
+
+#endif

+ 52 - 0
const.h

@@ -0,0 +1,52 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#ifndef _CONST_H_
+#define _CONST_H_
+
+
+#define APPNAME		"slim"
+
+#define DISPLAY		":0.0"
+
+#define CONSOLE_STR     "console"
+#define HALT_STR        "halt"
+#define REBOOT_STR      "reboot"
+#define EXIT_STR        "exit"
+#define SUSPEND_STR     "suspend"
+
+#define HIDE		0
+#define SHOW		1
+
+#define WAIT		0
+#define LOGIN		1
+#define FAIL		2
+#define CONSOLE		3
+#define REBOOT		4
+#define HALT		5
+#define EXIT		6
+#define SUSPEND		7
+
+#define GET_NAME    0
+#define GET_PASSWD	1
+
+#define	OK_EXIT		0
+#define	ERR_EXIT	1
+
+// duration for showing error messages, as "login command failed", in seconds
+#define ERROR_DURATION  5
+
+// variables replaced in login_cmd
+#define SESSION_VAR     "%session"
+#define THEME_VAR       "%theme"
+
+#endif
+

+ 664 - 0
image.cpp

@@ -0,0 +1,664 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+      
+   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.
+   
+   The following code has been adapted and extended from
+   xplanet 1.0.1, Copyright (C) 2002-04 Hari Nair <hari@alumni.caltech.edu>
+*/
+
+#include <cctype>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+using namespace std;
+
+#include "image.h"
+
+extern "C" {
+    int
+    read_png(const char *filename, int *width, int *height, unsigned char **rgb,
+             unsigned char **alpha);
+    int
+    read_jpeg(const char *filename, int *width, int *height, unsigned char **rgb);
+}
+
+Image::Image() : width(0), height(0), area(0),
+rgb_data(NULL), png_alpha(NULL), quality_(80) {}
+
+Image::Image(const int w, const int h, const unsigned char *rgb, const unsigned char *alpha) :
+width(w), height(h), area(w*h), quality_(80) {
+    width = w;
+    height = h;
+    area = w * h;
+
+    rgb_data = (unsigned char *) malloc(3 * area);
+    memcpy(rgb_data, rgb, 3 * area);
+
+    if (alpha == NULL) {
+        png_alpha = NULL;
+    } else {
+        png_alpha = (unsigned char *) malloc(area);
+        memcpy(png_alpha, alpha, area);
+    }
+}
+
+Image::~Image() {
+    free(rgb_data);
+    free(png_alpha);
+}
+
+bool
+Image::Read(const char *filename) {
+    char buf[4];
+    unsigned char *ubuf = (unsigned char *) buf;
+    int success = 0;
+
+    FILE *file;
+    file = fopen(filename, "rb");
+    if (file == NULL)
+        return(false);
+
+    /* see what kind of file we have */
+
+    fread(buf, 1, 4, file);
+    fclose(file);
+
+    if ((ubuf[0] == 0x89) && !strncmp("PNG", buf+1, 3)) {
+        success = read_png(filename, &width, &height, &rgb_data, &png_alpha);
+    }
+    else if ((ubuf[0] == 0xff) && (ubuf[1] == 0xd8)){
+        success = read_jpeg(filename, &width, &height, &rgb_data);
+    } else {
+        fprintf(stderr, "Unknown image format\n");
+        success = 0;
+    }
+    return(success == 1);
+}
+
+void
+Image::Reduce(const int factor) {
+    if (factor < 1)
+        return;
+
+    int scale = 1;
+    for (int i = 0; i < factor; i++)
+        scale *= 2;
+
+    double scale2 = scale*scale;
+
+    int w = width / scale;
+    int h = height / scale;
+    int new_area = w * h;
+
+    unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area);
+    memset(new_rgb, 0, 3 * new_area);
+
+    unsigned char *new_alpha = NULL;
+    if (png_alpha != NULL) {
+        new_alpha = (unsigned char *) malloc(new_area);
+        memset(new_alpha, 0, new_area);
+    }
+
+    int ipos = 0;
+    for (int j = 0; j < height; j++) {
+        int js = j / scale;
+        for (int i = 0; i < width; i++) {
+            int is = i/scale;
+            for (int k = 0; k < 3; k++)
+                new_rgb[3*(js * w + is) + k] += static_cast<unsigned char> ((rgb_data[3*ipos + k] + 0.5) / scale2);
+
+            if (png_alpha != NULL)
+                new_alpha[js * w + is] += static_cast<unsigned char> (png_alpha[ipos]/scale2);
+            ipos++;
+        }
+    }
+
+    free(rgb_data);
+    free(png_alpha);
+
+    rgb_data = new_rgb;
+    png_alpha = new_alpha;
+    width = w;
+    height = h;
+
+    area = w * h;
+}
+
+void
+Image::Resize(const int w, const int h) {
+    
+    if (width==w && height==h){
+        return;
+    }
+
+    int new_area = w * h;
+
+    unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area);
+    unsigned char *new_alpha = NULL;
+    if (png_alpha != NULL)
+        new_alpha = (unsigned char *) malloc(new_area);
+
+    const double scale_x = ((double) w) / width;
+    const double scale_y = ((double) h) / height;
+
+    int ipos = 0;
+    for (int j = 0; j < h; j++) {
+        const double y = j / scale_y;
+        for (int i = 0; i < w; i++) {
+            const double x = i / scale_x;
+            if (new_alpha == NULL)
+                getPixel(x, y, new_rgb + 3*ipos);
+            else
+                getPixel(x, y, new_rgb + 3*ipos, new_alpha + ipos);
+            ipos++;
+        }
+    }
+
+    free(rgb_data);
+    free(png_alpha);
+
+    rgb_data = new_rgb;
+    png_alpha = new_alpha;
+    width = w;
+    height = h;
+
+    area = w * h;
+}
+
+// Find the color of the desired point using bilinear interpolation.
+// Assume the array indices refer to the denter of the pixel, so each
+// pixel has corners at (i - 0.5, j - 0.5) and (i + 0.5, j + 0.5)
+void
+Image::getPixel(double x, double y, unsigned char *pixel) {
+    getPixel(x, y, pixel, NULL);
+}
+
+void
+Image::getPixel(double x, double y, unsigned char *pixel, unsigned char *alpha) {
+    if (x < -0.5)
+        x = -0.5;
+    if (x >= width - 0.5)
+        x = width - 0.5;
+
+    if (y < -0.5)
+        y = -0.5;
+    if (y >= height - 0.5)
+        y = height - 0.5;
+
+    int ix0 = (int) (floor(x));
+    int ix1 = ix0 + 1;
+    if (ix0 < 0)
+        ix0 = width - 1;
+    if (ix1 >= width)
+        ix1 = 0;
+
+    int iy0 = (int) (floor(y));
+    int iy1 = iy0 + 1;
+    if (iy0 < 0)
+        iy0 = 0;
+    if (iy1 >= height)
+        iy1 = height - 1;
+
+    const double t = x - floor(x);
+    const double u = 1 - (y - floor(y));
+
+    double weight[4];
+    weight[1] = t * u;
+    weight[0] = u - weight[1];
+    weight[2] = 1 - t - u + weight[1];
+    weight[3] = t - weight[1];
+
+    unsigned char *pixels[4];
+    pixels[0] = rgb_data + 3 * (iy0 * width + ix0);
+    pixels[1] = rgb_data + 3 * (iy0 * width + ix1);
+    pixels[2] = rgb_data + 3 * (iy1 * width + ix0);
+    pixels[3] = rgb_data + 3 * (iy1 * width + ix1);
+
+    memset(pixel, 0, 3);
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 3; j++)
+            pixel[j] += (unsigned char) (weight[i] * pixels[i][j]);
+    }
+
+    if (alpha != NULL) {
+        unsigned char pixels[4];
+        pixels[0] = png_alpha[iy0 * width + ix0];
+        pixels[1] = png_alpha[iy0 * width + ix1];
+        pixels[2] = png_alpha[iy0 * width + ix0];
+        pixels[3] = png_alpha[iy1 * width + ix1];
+
+        for (int i = 0; i < 4; i++)
+            *alpha = (unsigned char) (weight[i] * pixels[i]);
+    }
+}
+
+/* Merge the image with a background, taking care of the
+ * image Alpha transparency. (background alpha is ignored).
+ * The images is merged on position (x, y) on the
+ * background, the background must contain the image.
+ */
+void Image::Merge(Image* background, const int x, const int y) {
+
+    if (x + width > background->Width()|| y + height > background->Height()) {
+        return;
+    }
+
+    if (background->Width()*background->Height() != width*height)
+        background->Crop(x, y, width, height);
+
+    double tmp;
+    unsigned char *new_rgb = (unsigned char *) malloc(3 * width * height);
+    memset(new_rgb, 0, 3 * width * height);
+    const unsigned char *bg_rgb = background->getRGBData();
+    
+    int ipos = 0;
+    if (png_alpha != NULL){
+        for (int j = 0; j < height; j++) {
+            for (int i = 0; i < width; i++) {
+                for (int k = 0; k < 3; k++) {
+                    tmp = rgb_data[3*ipos + k]*png_alpha[ipos]/255.0
+                            + bg_rgb[3*ipos + k]*(1-png_alpha[ipos]/255.0);
+                    new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
+                }
+                ipos++;
+            }
+        }
+    } else {
+        for (int j = 0; j < height; j++) {
+            for (int i = 0; i < width; i++) {
+                for (int k = 0; k < 3; k++) {
+                    tmp = rgb_data[3*ipos + k];
+                    new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
+                }
+                ipos++;
+            }
+        }
+    }
+
+    free(rgb_data);
+    free(png_alpha);
+    rgb_data = new_rgb;
+    png_alpha = NULL;
+
+}
+
+/* Tile the image growing its size to the minimum entire
+ * multiple of w * h.
+ * The new dimensions should be > of the current ones.
+ * Note that this flattens image (alpha removed)
+ */
+void Image::Tile(const int w, const int h) {
+
+    if (w < width || h < height)
+        return;
+
+    int nx = w / width;
+    if (w % width > 0)
+        nx++;
+    int ny = h / height;
+    if (h % height > 0)
+        ny++;
+
+    int newwidth = nx*width;
+    int newheight=ny*height;
+        
+    unsigned char *new_rgb = (unsigned char *) malloc(3 * newwidth * newheight);
+    memset(new_rgb, 0, 3 * width * height * nx * ny);
+
+    int ipos = 0;
+    int opos = 0;
+
+    for (int r = 0; r < ny; r++) {
+        for (int c = 0; c < nx; c++) {
+            for (int j = 0; j < height; j++) {
+                for (int i = 0; i < width; i++) {
+                    opos = j*width + i;
+                    ipos = r*width*height*nx + j*newwidth + c*width +i;
+                    for (int k = 0; k < 3; k++) {
+                        new_rgb[3*ipos + k] = static_cast<unsigned char> (rgb_data[3*opos + k]);
+                    }
+                }
+            }
+        }
+    }
+
+    free(rgb_data);
+    free(png_alpha);
+    rgb_data = new_rgb;
+    png_alpha = NULL;
+    width = newwidth;
+    height = newheight;
+    area = width * height;
+    Crop(0,0,w,h);
+
+}
+
+/* Crop the image
+ */
+void Image::Crop(const int x, const int y, const int w, const int h) {
+
+    if (x+w > width || y+h > height) {
+        return;
+    }
+
+    int x2 = x + w;
+    int y2 = y + h;
+    unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h);
+    memset(new_rgb, 0, 3 * w * h);
+    unsigned char *new_alpha = (unsigned char *) malloc(w * h);
+    memset(new_alpha, 0, w * h);
+
+    int ipos = 0;
+    int opos = 0;
+
+    for (int j = 0; j < height; j++) {
+        for (int i = 0; i < width; i++) {
+            if (j>=y && i>=x && j<y2 && i<x2) {
+                for (int k = 0; k < 3; k++) {
+                    new_rgb[3*ipos + k] = static_cast<unsigned char> (rgb_data[3*opos + k]);
+                }
+                if (png_alpha != NULL)
+					new_alpha[ipos] = static_cast<unsigned char> (png_alpha[opos]);
+                ipos++;
+            }
+            opos++;
+        }
+    }
+
+    free(rgb_data);
+    free(png_alpha);
+    rgb_data = new_rgb;
+    if (png_alpha != NULL)
+	    png_alpha = new_alpha;
+    width = w;
+    height = h;
+    area = w * h;
+
+
+}
+
+/* Center the image in a rectangle of given width and height.
+ * Fills the remaining space (if any) with the hex color
+ */
+void Image::Center(const int w, const int h, const char *hex) {
+
+    unsigned long packed_rgb;
+    sscanf(hex, "%x", &packed_rgb);  
+
+    unsigned long r = packed_rgb>>16;
+    unsigned long g = packed_rgb>>8 & 0xff;
+    unsigned long b = packed_rgb & 0xff;    
+
+    unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h);
+    memset(new_rgb, 0, 3 * w * h);
+
+    int x = (w - width) / 2;
+    int y = (h - height) / 2;
+    
+    if (x<0) {
+    	Crop((width - w)/2,0,w,height);
+        x = 0;
+    }
+    if (y<0) {
+    	Crop(0,(height - h)/2,width,h);
+        y = 0;
+    }
+    int x2 = x + width;
+    int y2 = y + height;
+
+    int ipos = 0;
+    int opos = 0;
+    double tmp;
+
+    area = w * h;
+    for (int i = 0; i < area; i++) {
+        new_rgb[3*i] = r;
+        new_rgb[3*i+1] = g;
+        new_rgb[3*i+2] = b;
+    }
+
+	if (png_alpha != NULL) {
+        for (int j = 0; j < h; j++) {
+            for (int i = 0; i < w; i++) {
+                if (j>=y && i>=x && j<y2 && i<x2) {
+            	    ipos = j*w + i;
+                    for (int k = 0; k < 3; k++) {
+	                    tmp = rgb_data[3*opos + k]*png_alpha[opos]/255.0
+                              + new_rgb[k]*(1-png_alpha[opos]/255.0);
+                        new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
+                    }
+                    opos++;
+                }
+
+            }
+        }
+    } else {
+        for (int j = 0; j < h; j++) {
+            for (int i = 0; i < w; i++) {
+                if (j>=y && i>=x && j<y2 && i<x2) {
+            	    ipos = j*w + i;
+                    for (int k = 0; k < 3; k++) {
+	                    tmp = rgb_data[3*opos + k];
+                        new_rgb[3*ipos + k] = static_cast<unsigned char> (tmp);
+                    }
+                    opos++;
+                }
+
+            }
+        }
+    }
+    
+    free(rgb_data);
+    free(png_alpha);
+    rgb_data = new_rgb;
+    png_alpha = NULL;
+    width = w;
+    height = h;
+    
+}
+
+/* Fill the image with the given color and adjust its dimensions
+ * to passed values.
+ */
+void Image::Plain(const int w, const int h, const char *hex) {
+
+    unsigned long packed_rgb;
+    sscanf(hex, "%x", &packed_rgb);  
+
+    unsigned long r = packed_rgb>>16;
+    unsigned long g = packed_rgb>>8 & 0xff;
+    unsigned long b = packed_rgb & 0xff;    
+
+    unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h);
+    memset(new_rgb, 0, 3 * w * h);
+
+    area = w * h;
+    for (int i = 0; i < area; i++) {
+        new_rgb[3*i] = r;
+        new_rgb[3*i+1] = g;
+        new_rgb[3*i+2] = b;
+    }
+
+    free(rgb_data);
+    free(png_alpha);
+    rgb_data = new_rgb;
+    png_alpha = NULL;
+    width = w;
+    height = h;
+    
+}
+
+void
+Image::computeShift(unsigned long mask,
+                    unsigned char &left_shift,
+                    unsigned char &right_shift) {
+    left_shift = 0;
+    right_shift = 8;
+    if (mask != 0) {
+        while ((mask & 0x01) == 0) {
+            left_shift++;
+            mask >>= 1;
+        }
+        while ((mask & 0x01) == 1) {
+            right_shift--;
+            mask >>= 1;
+        }
+    }
+}
+
+Pixmap
+Image::createPixmap(Display* dpy, int scr, Window win) {
+    int i, j;   // loop variables
+
+    const int depth = DefaultDepth(dpy, scr);
+    Visual *visual = DefaultVisual(dpy, scr);
+    Colormap colormap = DefaultColormap(dpy, scr);
+
+    Pixmap tmp = XCreatePixmap(dpy, win, width, height,
+                               depth);
+
+    char *pixmap_data = NULL;
+    switch (depth) {
+    case 32:
+    case 24:
+        pixmap_data = new char[4 * width * height];
+        break;
+    case 16:
+    case 15:
+        pixmap_data = new char[2 * width * height];
+        break;
+    case 8:
+        pixmap_data = new char[width * height];
+        break;
+    default:
+        break;
+    }
+
+    XImage *ximage = XCreateImage(dpy, visual, depth, ZPixmap, 0,
+                                  pixmap_data, width, height,
+                                  8, 0);
+
+    int entries;
+    XVisualInfo v_template;
+    v_template.visualid = XVisualIDFromVisual(visual);
+    XVisualInfo *visual_info = XGetVisualInfo(dpy, VisualIDMask,
+                               &v_template, &entries);
+
+    unsigned long ipos = 0;
+    switch (visual_info->c_class) {
+    case PseudoColor: {
+            XColor xc;
+            xc.flags = DoRed | DoGreen | DoBlue;
+
+            int num_colors = 256;
+            XColor *colors = new XColor[num_colors];
+            for (i = 0; i < num_colors; i++)
+                colors[i].pixel = (unsigned long) i;
+            XQueryColors(dpy, colormap, colors, num_colors);
+
+            int *closest_color = new int[num_colors];
+
+            for (i = 0; i < num_colors; i++) {
+                xc.red = (i & 0xe0) << 8;           // highest 3 bits
+                xc.green = (i & 0x1c) << 11;        // middle 3 bits
+                xc.blue = (i & 0x03) << 14;         // lowest 2 bits
+
+                // find the closest color in the colormap
+                double distance, distance_squared, min_distance = 0;
+                for (int ii = 0; ii < num_colors; ii++) {
+                    distance = colors[ii].red - xc.red;
+                    distance_squared = distance * distance;
+                    distance = colors[ii].green - xc.green;
+                    distance_squared += distance * distance;
+                    distance = colors[ii].blue - xc.blue;
+                    distance_squared += distance * distance;
+
+                    if ((ii == 0) || (distance_squared <= min_distance)) {
+                        min_distance = distance_squared;
+                        closest_color[i] = ii;
+                    }
+                }
+            }
+
+            for (j = 0; j < height; j++) {
+                for (i = 0; i < width; i++) {
+                    xc.red = (unsigned short) (rgb_data[ipos++] & 0xe0);
+                    xc.green = (unsigned short) (rgb_data[ipos++] & 0xe0);
+                    xc.blue = (unsigned short) (rgb_data[ipos++] & 0xc0);
+
+                    xc.pixel = xc.red | (xc.green >> 3) | (xc.blue >> 6);
+                    XPutPixel(ximage, i, j,
+                              colors[closest_color[xc.pixel]].pixel);
+                }
+            }
+            delete [] colors;
+            delete [] closest_color;
+        }
+        break;
+    case TrueColor: {
+            unsigned char red_left_shift;
+            unsigned char red_right_shift;
+            unsigned char green_left_shift;
+            unsigned char green_right_shift;
+            unsigned char blue_left_shift;
+            unsigned char blue_right_shift;
+
+            computeShift(visual_info->red_mask, red_left_shift,
+                         red_right_shift);
+            computeShift(visual_info->green_mask, green_left_shift,
+                         green_right_shift);
+            computeShift(visual_info->blue_mask, blue_left_shift,
+                         blue_right_shift);
+
+            unsigned long pixel;
+            unsigned long red, green, blue;
+            for (j = 0; j < height; j++) {
+                for (i = 0; i < width; i++) {
+                    red = (unsigned long)
+                          rgb_data[ipos++] >> red_right_shift;
+                    green = (unsigned long)
+                            rgb_data[ipos++] >> green_right_shift;
+                    blue = (unsigned long)
+                           rgb_data[ipos++] >> blue_right_shift;
+
+                    pixel = (((red << red_left_shift) & visual_info->red_mask)
+                             | ((green << green_left_shift)
+                                & visual_info->green_mask)
+                             | ((blue << blue_left_shift)
+                                & visual_info->blue_mask));
+
+                    XPutPixel(ximage, i, j, pixel);
+                }
+            }
+        }
+        break;
+    default: {
+            cerr << "Login.app: could not load image" << endl;
+            return(tmp);
+        }
+    }
+
+    GC gc = XCreateGC(dpy, win, 0, NULL);
+    XPutImage(dpy, tmp, gc, ximage, 0, 0, 0, 0, width, height);
+
+    XFreeGC(dpy, gc);
+
+    XFree(visual_info);
+
+    delete [] pixmap_data;
+
+    // Set ximage data to NULL since pixmap data was deallocated above
+    ximage->data = NULL;
+    XDestroyImage(ximage);
+
+    return(tmp);
+}
+

+ 73 - 0
image.h

@@ -0,0 +1,73 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+      
+   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.
+   
+   The following code has been adapted and extended from
+   xplanet 1.0.1, Copyright (C) 2002-04 Hari Nair <hari@alumni.caltech.edu>
+*/
+
+#ifndef _IMAGE_H_
+#define _IMAGE_H_
+
+#include <X11/Xlib.h>
+#include <X11/Xmu/WinUtil.h>
+
+class Image {
+public:
+    Image();
+    Image(const int w, const int h, const unsigned char *rgb,
+          const unsigned char *alpha);
+
+    ~Image();
+
+    const unsigned char * getPNGAlpha() const {
+        return(png_alpha);
+    };
+    const unsigned char * getRGBData() const {
+        return(rgb_data);
+    };
+
+    void getPixel(double px, double py, unsigned char *pixel);
+    void getPixel(double px, double py, unsigned char *pixel,
+                  unsigned char *alpha);
+
+    int Width() const  {
+        return(width);
+    };
+    int Height() const {
+        return(height);
+    };
+    void Quality(const int q) {
+        quality_ = q;
+    };
+
+    bool Read(const char *filename);
+
+    void Reduce(const int factor);
+    void Resize(const int w, const int h);
+    void Merge(Image* background, const int x, const int y);
+    void Crop(const int x, const int y, const int w, const int h);
+    void Tile(const int w, const int h);
+    void Center(const int w, const int h, const char *hex);
+    void Plain(const int w, const int h, const char *hex);
+    
+    void computeShift(unsigned long mask, unsigned char &left_shift,
+                      unsigned char &right_shift);
+
+    Pixmap createPixmap(Display* dpy, int scr, Window win);
+
+
+private:
+    int width, height, area;
+    unsigned char *rgb_data;
+    unsigned char *png_alpha;
+
+    int quality_;
+};
+
+#endif

+ 249 - 0
input.cpp

@@ -0,0 +1,249 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#include "input.h"
+#include <cstdlib>
+
+Input::Input(Cfg* c) {
+    NameBuffer[0] = '\0';
+    PasswdBuffer[0] = '\0';
+    HiddenPasswdBuffer[0] = '\0';
+    Field = GET_NAME;
+    Action = WAIT;
+    cfg = c;
+}
+
+
+Input::~Input() {
+    // Does nothing
+}
+
+
+char Input::Key(char ascii, KeySym keysym, bool singleInputMode) {
+    char tmp = 0;
+
+    // Take a screenshot
+    if(keysym == XK_F11) {
+        system(cfg->getOption("screenshot_cmd").c_str());
+    }
+
+    if  (!singleInputMode && (keysym == XK_Tab || keysym == XK_ISO_Left_Tab)) {
+        if (Field == GET_NAME) {
+            // Move to next field
+            Field = GET_PASSWD;
+        } else {
+            Field = GET_NAME;
+        }
+    } else if(keysym == XK_Return) {
+        if(!strcmp(NameBuffer, ""))
+            return tmp;
+
+        // Check for special command (console, halt, reboot, exit)
+        int special = SpecialWanted();
+
+        if(Field == GET_NAME) {
+            // Move to next field
+            Field = GET_PASSWD;
+
+            // Check for special command (console, exit)
+            if(special == CONSOLE || special == EXIT)
+                Action = special;
+        } else {
+            // Check for special command (halt, reboot)
+            if(special == REBOOT || special == HALT || special == SUSPEND)
+                Action = SpecialCorrect(special);
+
+            // Regular login
+            else {
+                if(Correct())
+                    Action = LOGIN;
+                else
+                    Action = FAIL;
+            }
+        }
+    } else if(keysym == XK_Delete || keysym == XK_BackSpace)
+        tmp = DeleteLast();
+    else if(isprint(ascii))
+        if(keysym < XK_Shift_L || keysym > XK_Hyper_R)
+            Add(ascii);
+
+    return tmp;
+}
+
+
+int Input::GetAction() {
+    return Action;
+}
+
+
+int Input::GetField() {
+    return Field;
+}
+
+
+char* Input::GetName() {
+    return NameBuffer;
+}
+
+
+char* Input::GetHiddenPasswd() {
+    return HiddenPasswdBuffer;
+}
+
+
+struct passwd* Input::GetPasswdStruct() {
+    struct passwd* pw = getpwnam(NameBuffer);
+    endpwent();
+    if (pw->pw_shell[0] == '\0') {
+        setusershell();
+        pw->pw_shell = getusershell();
+        endusershell();
+    }
+    return pw;
+}
+
+
+void Input::Add(char ascii) {
+    char tmp[2];
+    tmp[0] = ascii;
+    tmp[1] = '\0';
+
+    switch(Field) {
+    case GET_NAME:
+        if(strlen(NameBuffer) < INPUT_MAXLENGTH_NAME-1)
+            strcat(NameBuffer, tmp);
+        break;
+    case GET_PASSWD:
+        if(strlen(PasswdBuffer) < INPUT_MAXLENGTH_PASSWD-1) {
+            strcat(PasswdBuffer, tmp);
+            tmp[0] = '*';
+            strcat(HiddenPasswdBuffer, tmp);
+        }
+        break;
+    }
+}
+
+
+char Input::DeleteLast() {
+    char tmp = 0;
+    int len = 0;
+
+    switch(Field) {
+    case GET_NAME:
+        len = strlen(NameBuffer) - 1;
+        tmp = NameBuffer[len];
+        NameBuffer[len] = '\0';
+        break;
+    case GET_PASSWD:
+        len = strlen(PasswdBuffer) - 1;
+        tmp = '*';
+        PasswdBuffer[len] = '\0';
+        HiddenPasswdBuffer[len] = '\0';
+        break;
+    }
+
+    return tmp;
+}
+
+
+void Input::Reset() {
+    ResetName();
+    ResetPassword();
+    Field = GET_NAME;
+    Action = WAIT;
+}
+
+void Input::ResetName() {
+    NameBuffer[0] = '\0';
+    Action = WAIT;
+}
+
+void Input::ResetPassword() {
+    PasswdBuffer[0] = '\0';
+    HiddenPasswdBuffer[0] = '\0';
+    Action = WAIT;
+}
+
+// used for automatically fill name at startup
+void Input::SetName(string name) {
+    NameBuffer[0] = '\0';
+    if(name.size() < INPUT_MAXLENGTH_NAME) {
+        strcat(NameBuffer, name.c_str());
+    }
+    Field = GET_PASSWD;
+    Action = WAIT;
+}
+
+int Input::Correct() {
+    char *unencrypted, *encrypted, *correct;
+    struct passwd *pw;
+
+    pw = getpwnam(NameBuffer);
+    endpwent();
+    if(pw == 0)
+        return 0;
+
+#ifdef HAVE_SHADOW
+    struct spwd *sp = getspnam(pw->pw_name);    
+    endspent();
+    if(sp)
+	correct = sp->sp_pwdp;
+    else
+#endif
+	correct = pw->pw_passwd;
+
+    if(correct == 0 || correct[0] == '\0')
+        return 1;
+
+    unencrypted = PasswdBuffer;
+    encrypted = crypt(unencrypted, correct);
+    memset(unencrypted, 0, strlen (unencrypted));
+    return (strcmp(encrypted, correct) == 0);
+}
+
+
+int Input::SpecialWanted() {
+    int result;
+
+    if(!strcmp(NameBuffer, CONSOLE_STR))
+        result = CONSOLE;
+    else if(!strcmp(NameBuffer, HALT_STR))
+        result = HALT;
+    else if(!strcmp(NameBuffer, REBOOT_STR))
+        result = REBOOT;
+    else if(!strcmp(NameBuffer, SUSPEND_STR))
+        result = SUSPEND;
+    else if(!strcmp(NameBuffer, EXIT_STR))
+        result = EXIT;
+    else
+        result = 0;
+
+    return result;
+}
+
+
+int Input::SpecialCorrect(int special) {
+    int result, c;
+    char tmp[INPUT_MAXLENGTH_NAME];
+
+    strcpy(tmp, NameBuffer);
+    strcpy(NameBuffer, "root");
+    c = Correct();
+    strcpy(NameBuffer, tmp);
+
+    if(c)
+        result = special;
+    else
+        result = FAIL;
+
+    return result;
+}
+

+ 63 - 0
input.h

@@ -0,0 +1,63 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#ifndef _INPUT_H_
+#define _INPUT_H_
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xft/Xft.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <string>
+#include <ctype.h>
+#include <iostream>
+
+#include "const.h"
+#include "cfg.h"
+
+#ifdef HAVE_SHADOW
+#include <shadow.h>
+#endif
+
+class Input {
+public:
+    Input(Cfg* c);
+    ~Input();
+    char Key(char ascii, KeySym keysym, bool singleInputMode);
+    int GetAction();
+    int GetField();
+    char* GetName();
+    char* GetHiddenPasswd();
+    struct passwd* GetPasswdStruct();
+    void Reset();
+    void ResetName();
+    void ResetPassword();
+    void SetName(string name);
+
+private:
+    void Add(char ascii);
+    char DeleteLast();
+    int Correct();
+    int SpecialWanted();
+    int SpecialCorrect(int special);
+    Cfg* cfg;
+
+    char NameBuffer[INPUT_MAXLENGTH_NAME];
+    char PasswdBuffer[INPUT_MAXLENGTH_PASSWD];
+    char HiddenPasswdBuffer[INPUT_MAXLENGTH_PASSWD];
+    int Action;
+    int Field;
+
+};
+
+#endif
+

+ 93 - 0
jpeg.c

@@ -0,0 +1,93 @@
+/****************************************************************************
+    jpeg.c - read and write jpeg images using libjpeg routines
+    Copyright (C) 2002 Hari Nair <hari@alumni.caltech.edu>
+
+    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
+****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <jpeglib.h>
+
+int
+read_jpeg(const char *filename, int *width, int *height, unsigned char **rgb)
+{
+    struct jpeg_decompress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    unsigned char *ptr = NULL;
+    unsigned int i, ipos;
+
+    FILE *infile = fopen(filename, "rb");
+
+    cinfo.err = jpeg_std_error(&jerr);
+    jpeg_create_decompress(&cinfo);
+    jpeg_stdio_src(&cinfo, infile);
+    jpeg_read_header(&cinfo, TRUE);
+    jpeg_start_decompress(&cinfo);
+
+    *width = cinfo.output_width;
+    *height = cinfo.output_height;
+
+    rgb[0] = malloc(3 * cinfo.output_width * cinfo.output_height);
+    if (rgb[0] == NULL)
+    {
+        fprintf(stderr, "Can't allocate memory for JPEG file.\n");
+	fclose(infile);
+        return(0);
+    }
+
+    if (cinfo.output_components == 3)
+    {
+        ptr = rgb[0];
+        while (cinfo.output_scanline < cinfo.output_height) 
+        {
+            jpeg_read_scanlines(&cinfo, &ptr, 1);
+            ptr += 3 * cinfo.output_width;
+        }
+    }
+    else if (cinfo.output_components == 1)
+    {
+        ptr = malloc(cinfo.output_width);
+        if (ptr == NULL)
+        {
+            fprintf(stderr, "Can't allocate memory for JPEG file.\n");
+	    fclose(infile);
+            return(0);
+        }
+
+        ipos = 0;
+        while (cinfo.output_scanline < cinfo.output_height) 
+        {
+            jpeg_read_scanlines(&cinfo, &ptr, 1);
+
+            for (i = 0; i < cinfo.output_width; i++)
+            {
+                memset(rgb[0] + ipos, ptr[i], 3);
+                ipos += 3;
+            }
+        }
+
+        free(ptr);
+    }
+
+    jpeg_finish_decompress(&cinfo);
+    jpeg_destroy_decompress(&cinfo);
+
+    fclose(infile);
+
+    return(1);
+}

+ 22 - 0
main.cpp

@@ -0,0 +1,22 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+      
+   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.
+*/
+
+#include "app.h"
+#include "const.h"
+
+App* LoginApp = 0;
+
+int main(int argc, char** argv) {
+    LoginApp = new App(argc, argv);
+    LoginApp->Run();
+    return 0;
+}
+

+ 550 - 0
panel.cpp

@@ -0,0 +1,550 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#include <sstream>
+#include "panel.h"
+
+using namespace std;
+
+Panel::Panel(Display* dpy, int scr, Window root, Cfg* config,
+             const string& themedir) {
+    // Set display
+    Dpy = dpy;
+    Scr = scr;
+    Root = root;
+    cfg = config;
+
+    session = "";
+
+    // Init GC
+    XGCValues gcv;
+    unsigned long gcm;
+    gcm = GCForeground|GCBackground|GCGraphicsExposures;
+    gcv.foreground = GetColor("black");
+    gcv.background = GetColor("white");
+    gcv.graphics_exposures = False;
+    TextGC = XCreateGC(Dpy, Root, gcm, &gcv);
+
+    font = XftFontOpenName(Dpy, Scr, cfg->getOption("input_font").c_str());
+    welcomefont = XftFontOpenName(Dpy, Scr, cfg->getOption("welcome_font").c_str());
+    introfont = XftFontOpenName(Dpy, Scr, cfg->getOption("intro_font").c_str());
+    enterfont = XftFontOpenName(Dpy, Scr, cfg->getOption("username_font").c_str());
+    msgfont = XftFontOpenName(Dpy, Scr, cfg->getOption("msg_font").c_str());
+
+    Visual* visual = DefaultVisual(Dpy, Scr);
+    Colormap colormap = DefaultColormap(Dpy, Scr);
+    // NOTE: using XftColorAllocValue() would be a better solution. Lazy me.
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("input_color").c_str(), &inputcolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("input_shadow_color").c_str(), &inputshadowcolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("welcome_color").c_str(), &welcomecolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("welcome_shadow_color").c_str(), &welcomeshadowcolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("username_color").c_str(), &entercolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("username_shadow_color").c_str(), &entershadowcolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("msg_color").c_str(), &msgcolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("msg_shadow_color").c_str(), &msgshadowcolor);
+    XftColorAllocName(Dpy, visual, colormap, cfg->getOption("intro_color").c_str(), &introcolor);
+
+    // Load properties from config / theme
+    input_name_x = Cfg::string2int(cfg->getOption("input_name_x").c_str());
+    input_name_y = Cfg::string2int(cfg->getOption("input_name_y").c_str());
+    input_pass_x = Cfg::string2int(cfg->getOption("input_pass_x").c_str());
+    input_pass_y = Cfg::string2int(cfg->getOption("input_pass_y").c_str());
+    inputShadowXOffset =
+        Cfg::string2int(cfg->getOption("input_shadow_xoffset").c_str());
+    inputShadowYOffset =
+        Cfg::string2int(cfg->getOption("input_shadow_yoffset").c_str());
+
+    if (input_pass_x < 0 || input_pass_y < 0){ // single inputbox mode
+        input_pass_x = input_name_x;
+        input_pass_y = input_name_y;
+    }
+
+    // Load panel and background image
+    string panelpng = "";
+    panelpng = panelpng + themedir +"/panel.png";
+    image = new Image;
+    bool loaded = image->Read(panelpng.c_str());
+    if (!loaded) { // try jpeg if png failed
+        panelpng = themedir + "/panel.jpg";
+        loaded = image->Read(panelpng.c_str());
+        if (!loaded) {
+            cerr << APPNAME << ": could not load panel image for theme '"
+		 << basename((char*)themedir.c_str()) << "'"
+		 << endl;
+            exit(ERR_EXIT);
+        }
+    }
+
+    Image* bg = new Image();
+    string bgstyle = cfg->getOption("background_style");
+    if (bgstyle != "color") {
+        panelpng = themedir +"/background.png";
+        loaded = bg->Read(panelpng.c_str());
+        if (!loaded) { // try jpeg if png failed
+            panelpng = themedir + "/background.jpg";
+            loaded = bg->Read(panelpng.c_str());
+            if (!loaded){
+                cerr << APPNAME << ": could not load background image for theme '"
+		        << basename((char*)themedir.c_str()) << "'"
+		        << endl;
+                exit(ERR_EXIT);
+            }
+        }
+    }
+    if (bgstyle == "stretch") {
+        bg->Resize(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
+    } else if (bgstyle == "tile") {
+        bg->Tile(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)));
+    } else if (bgstyle == "center") {
+ 	    string hexvalue = cfg->getOption("background_color");
+        hexvalue = hexvalue.substr(1,6);
+  	    bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+       			    hexvalue.c_str());
+    } else { // plain color or error
+	    string hexvalue = cfg->getOption("background_color");
+        hexvalue = hexvalue.substr(1,6);
+  	    bg->Center(XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)),
+    			    hexvalue.c_str());
+    }
+
+    string cfgX = cfg->getOption("input_panel_x");
+    string cfgY = cfg->getOption("input_panel_y");
+    X = Cfg::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), image->Width());
+    Y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), image->Height());
+
+    // Merge image into background
+    image->Merge(bg, X, Y);
+    PanelPixmap = image->createPixmap(Dpy, Scr, Root);
+
+    // Read (and substitute vars in) the welcome message
+    welcome_message = cfg->getWelcomeMessage();
+    intro_message = cfg->getOption("intro_msg");
+
+    // Init In
+    In = new Input(cfg);
+}
+
+Panel::~Panel() {
+    XftColorFree (Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &inputcolor);
+    XftColorFree (Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &msgcolor);
+    XftColorFree (Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &welcomecolor);
+    XftColorFree (Dpy, DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr), &entercolor);
+    XFreeGC(Dpy, TextGC);
+    delete In;
+    delete image;
+
+}
+
+void Panel::OpenPanel() {
+    // Create window
+    Win = XCreateSimpleWindow(Dpy, Root, X, Y,
+                              image->Width(),
+                              image->Height(),
+                              0, GetColor("white"), GetColor("white"));
+
+    // Events
+    XSelectInput(Dpy, Win, ExposureMask | KeyPressMask);
+
+    // Set background
+    XSetWindowBackgroundPixmap(Dpy, Win, PanelPixmap);
+
+    // Show window
+    XMapWindow(Dpy, Win);
+    XMoveWindow(Dpy, Win, X, Y); // override wm positioning (for tests)
+
+    // Grab keyboard
+    XGrabKeyboard(Dpy, Win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
+
+    XFlush(Dpy);
+
+}
+
+void Panel::ClosePanel() {
+    XUngrabKeyboard(Dpy, CurrentTime);
+    XUnmapWindow(Dpy, Win);
+    XDestroyWindow(Dpy, Win);
+    XFlush(Dpy);
+}
+
+void Panel::ClearPanel() {
+    session = "";
+    In->Reset();
+    XClearWindow(Dpy, Root);
+    XClearWindow(Dpy, Win);
+    Cursor(SHOW);
+    ShowText();
+    XFlush(Dpy);
+}
+
+void Panel::Message(const char* text) {
+    string cfgX, cfgY;
+    XGlyphInfo extents;
+    XftDraw *draw = XftDrawCreate(Dpy, Root,
+                                  DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
+    XftTextExtents8(Dpy, msgfont, (XftChar8*)text,
+                    strlen(text), &extents);
+    cfgX = cfg->getOption("msg_x");
+    cfgY = cfg->getOption("msg_y");
+    int shadowXOffset =
+        Cfg::string2int(cfg->getOption("msg_shadow_xoffset").c_str());
+    int shadowYOffset =
+        Cfg::string2int(cfg->getOption("msg_shadow_yoffset").c_str());
+
+    int msg_x = Cfg::absolutepos(cfgX, XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.width);
+    int msg_y = Cfg::absolutepos(cfgY, XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.height);
+
+    SlimDrawString8 (draw, &msgcolor, msgfont, msg_x, msg_y,
+                     (XftChar8*)text, strlen(text),
+                     &msgshadowcolor,
+                     shadowXOffset, shadowYOffset);
+    XFlush(Dpy);
+    XftDrawDestroy(draw);
+}
+
+void Panel::Error(const char* text) {
+    ClosePanel();
+    Message(text);
+    sleep(ERROR_DURATION);
+    OpenPanel();
+    ClearPanel();
+}
+
+
+Input* Panel::GetInput() {
+    return In;
+}
+
+unsigned long Panel::GetColor(const char* colorname) {
+    XColor color;
+    XWindowAttributes attributes;
+
+    XGetWindowAttributes(Dpy, Root, &attributes);
+    color.pixel = 0;
+
+    if(!XParseColor(Dpy, attributes.colormap, colorname, &color))
+        cerr << APPNAME << ": can't parse color " << colorname << endl;
+    else if(!XAllocColor(Dpy, attributes.colormap, &color))
+        cerr << APPNAME << ": can't allocate color " << colorname << endl;
+
+    return color.pixel;
+}
+
+void Panel::Cursor(int visible) {
+    char* text;
+    int xx, yy, x2,y2, cheight;
+    char* txth = "Wj"; // used to get cursor height
+
+    switch(In->GetField()) {
+        case GET_PASSWD:
+            text = In->GetHiddenPasswd();
+            xx = input_pass_x;
+            yy = input_pass_y;
+            break;
+
+        case GET_NAME:
+            text = In->GetName();
+            xx = input_name_x;
+            yy = input_name_y;
+            break;
+    }
+
+
+    XGlyphInfo extents;
+    XftTextExtents8(Dpy, font, (XftChar8*)txth, strlen(txth), &extents);
+    cheight = extents.height;
+    y2 = yy - extents.y + extents.height;
+    XftTextExtents8(Dpy, font, (XftChar8*)text, strlen(text), &extents);
+    xx += extents.width;
+
+    if(visible == SHOW) {
+        XSetForeground(Dpy, TextGC,
+                       GetColor(cfg->getOption("input_color").c_str()));
+        XDrawLine(Dpy, Win, TextGC,
+                  xx+1, yy-cheight,
+                  xx+1, y2);
+    } else {
+        XClearArea(Dpy, Win, xx+1, yy-cheight,
+                   1, y2-(yy-cheight)+1, false);
+    }
+}
+
+int Panel::EventHandler(XEvent* event) {
+    Action = WAIT;
+
+    switch(event->type) {
+    case Expose:
+        OnExpose(event);
+        break;
+
+    case KeyPress:
+        OnKeyPress(event);
+        break;
+    }
+
+    return Action;
+}
+
+void Panel::OnExpose(XEvent* event) {
+    char* name = In->GetName();
+    char* passwd = In->GetHiddenPasswd();
+    XftDraw *draw = XftDrawCreate(Dpy, Win,
+                        DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
+    if (input_pass_x != input_name_x || input_pass_y != input_name_y){
+        SlimDrawString8 (draw, &inputcolor, font, input_name_x, input_name_y,
+                         (XftChar8*)name, strlen(name),
+                         &inputshadowcolor,
+                         inputShadowXOffset, inputShadowYOffset);
+        SlimDrawString8 (draw, &inputcolor, font, input_pass_x, input_pass_y,
+                         (XftChar8*)passwd, strlen(passwd),
+                         &inputshadowcolor,
+                         inputShadowXOffset, inputShadowYOffset);
+    } else { //single input mode
+        switch(In->GetField()) {
+            case GET_PASSWD:
+                SlimDrawString8 (draw, &inputcolor, font,
+                                 input_pass_x, input_pass_y,
+                                 (XftChar8*)passwd, strlen(passwd),
+                                 &inputshadowcolor,
+                                 inputShadowXOffset, inputShadowYOffset);
+                break;
+            case GET_NAME:
+                SlimDrawString8 (draw, &inputcolor, font,
+                                 input_name_x, input_name_y,
+                                 (XftChar8*)name, strlen(name),
+                                 &inputshadowcolor,
+                                 inputShadowXOffset, inputShadowYOffset);
+                break;
+        }
+    }
+
+    XftDrawDestroy (draw);
+    Cursor(SHOW);
+    ShowText();
+}
+
+void Panel::OnKeyPress(XEvent* event) {
+    char del;
+    char buffer;
+    KeySym keysym;
+    XComposeStatus compstatus;
+    int xx;
+    int yy;
+    char* text;
+
+    bool singleInputMode =
+        input_name_x == input_pass_x &&
+        input_name_y == input_pass_y;
+    Cursor(HIDE);
+    XLookupString(&event->xkey, &buffer, 1, &keysym, &compstatus);
+    del = In->Key(buffer, keysym, singleInputMode);
+    Action = In->GetAction();
+
+    XGlyphInfo extents, delextents;
+    XftDraw *draw = XftDrawCreate(Dpy, Win,
+                                  DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
+
+    if (keysym == XK_F1) {
+        SwitchSession();
+    }
+
+    bool clearField = false;
+    string formerString = "";
+    if ((((XKeyEvent*)event)->state & ControlMask)) {
+        if (keysym == XK_w || keysym == XK_u) {
+            clearField = true;
+        }
+    }
+
+    switch(In->GetField()) {
+        case GET_PASSWD:
+            if (strlen(In->GetHiddenPasswd()) == 0){
+                // clear name and welcome label if we just entered the
+                // password field
+                if (singleInputMode) {
+                    xx = input_name_x;
+                    yy = input_name_y;
+                    text = In->GetName();
+                    XftTextExtents8(Dpy, font, (XftChar8*)text,
+                                    strlen(text), &extents);
+                    XClearWindow(Dpy, Win);
+                    ShowText();
+                }
+            }
+
+            if (clearField) {
+                formerString = In->GetHiddenPasswd();
+                In->ResetPassword();
+            }
+            text = In->GetHiddenPasswd();
+            xx = input_pass_x;
+            yy = input_pass_y;
+            break;
+
+        case GET_NAME:
+            if (clearField) {
+                formerString = In->GetName();
+                In->ResetName();
+            }
+            text = In->GetName();
+            xx = input_name_x;
+            yy = input_name_y;
+            break;
+    }
+
+    char* txth = "Wj"; // get proper maximum height ?
+    XftTextExtents8(Dpy, font, (XftChar8*)txth, strlen(txth), &extents);
+    int maxHeight = extents.height;
+
+    string tmp = "";
+    if (clearField) {
+        tmp = formerString;
+    } else {
+        tmp = text;
+        tmp = tmp + del;
+    }
+    XftTextExtents8(Dpy, font, (XftChar8*)tmp.c_str(),
+                    strlen(tmp.c_str()), &extents);
+    int maxLength = extents.width;
+
+    XClearArea(Dpy, Win, xx-3, yy-maxHeight-3,
+               maxLength+6, maxHeight+6, false);
+
+    if (!clearField) {
+        SlimDrawString8 (draw, &inputcolor, font, xx, yy,
+                         (XftChar8*)text, strlen(text),
+                         &inputshadowcolor,
+                         inputShadowXOffset, inputShadowYOffset);
+    }
+
+    XftDrawDestroy (draw);
+    Cursor(SHOW);
+}
+
+// Draw welcome and "enter username" message
+void Panel::ShowText(){
+    string cfgX, cfgY;
+    int n=-1;
+    XGlyphInfo extents;
+
+    bool singleInputMode =
+    input_name_x == input_pass_x &&
+    input_name_y == input_pass_y;
+
+    XftDraw *draw = XftDrawCreate(Dpy, Win,
+                                  DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
+    /* welcome message */
+    XftTextExtents8(Dpy, welcomefont, (XftChar8*)welcome_message.c_str(),
+                    strlen(welcome_message.c_str()), &extents);
+    cfgX = cfg->getOption("welcome_x");
+    cfgY = cfg->getOption("welcome_y");
+    int shadowXOffset =
+        Cfg::string2int(cfg->getOption("welcome_shadow_xoffset").c_str());
+    int shadowYOffset =
+        Cfg::string2int(cfg->getOption("welcome_shadow_yoffset").c_str());
+    welcome_x = Cfg::absolutepos(cfgX, image->Width(), extents.width);
+    welcome_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
+    if (welcome_x >= 0 && welcome_y >= 0) {
+        SlimDrawString8 (draw, &welcomecolor, welcomefont,
+                         welcome_x, welcome_y,
+                         (XftChar8*)welcome_message.c_str(),
+                         strlen(welcome_message.c_str()),
+                         &welcomeshadowcolor, shadowXOffset, shadowYOffset);
+    }
+
+    /* Enter username-password message */
+    string msg;
+    if (!singleInputMode|| In->GetField() == GET_PASSWD ) {
+        msg = cfg->getOption("password_msg");
+        XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(),
+                        strlen(msg.c_str()), &extents);
+        cfgX = cfg->getOption("password_x");
+        cfgY = cfg->getOption("password_y");
+        int shadowXOffset =
+            Cfg::string2int(cfg->getOption("username_shadow_xoffset").c_str());
+        int shadowYOffset =
+            Cfg::string2int(cfg->getOption("username_shadow_yoffset").c_str());
+        password_x = Cfg::absolutepos(cfgX, image->Width(), extents.width);
+        password_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
+        if (password_x >= 0 && password_y >= 0){
+            SlimDrawString8 (draw, &entercolor, enterfont, password_x, password_y,
+                             (XftChar8*)msg.c_str(), strlen(msg.c_str()),
+                             &entershadowcolor, shadowXOffset, shadowYOffset);
+        }
+    }
+    if (!singleInputMode|| In->GetField() == GET_NAME ) {
+        msg = cfg->getOption("username_msg");
+        XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(),
+                        strlen(msg.c_str()), &extents);
+        cfgX = cfg->getOption("username_x");
+        cfgY = cfg->getOption("username_y");
+        int shadowXOffset =
+            Cfg::string2int(cfg->getOption("username_shadow_xoffset").c_str());
+        int shadowYOffset =
+            Cfg::string2int(cfg->getOption("username_shadow_yoffset").c_str());
+        username_x = Cfg::absolutepos(cfgX, image->Width(), extents.width);
+        username_y = Cfg::absolutepos(cfgY, image->Height(), extents.height);
+        if (username_x >= 0 && username_y >= 0){
+            SlimDrawString8 (draw, &entercolor, enterfont, username_x, username_y,
+                             (XftChar8*)msg.c_str(), strlen(msg.c_str()),
+                             &entershadowcolor, shadowXOffset, shadowYOffset);
+        }
+    }
+    XftDrawDestroy(draw);
+}
+
+string Panel::getSession() {
+    return session;
+}
+
+// choose next available session type
+void Panel::SwitchSession() {
+    session = cfg->nextSession(session);
+    //TODO: get sessions from cfg and cycle to the next one
+    ShowSession();
+}
+
+// Display session type on the screen
+void Panel::ShowSession() {
+    XClearWindow(Dpy, Root);
+    string currsession = "Session: " + session;
+    char* text = (char*) currsession.c_str();
+    XGlyphInfo extents;
+    XftDraw *draw = XftDrawCreate(Dpy, Root,
+                                  DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr));
+    XftTextExtents8(Dpy, msgfont, (XftChar8*)text,
+                    strlen(text), &extents);
+    int msg_x = Cfg::absolutepos("50%", XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), extents.width);
+    int msg_y = XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)) - extents.height -100;
+    int shadowXOffset =
+        Cfg::string2int(cfg->getOption("welcome_shadow_xoffset").c_str());
+    int shadowYOffset =
+        Cfg::string2int(cfg->getOption("welcome_shadow_yoffset").c_str());
+
+    SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y,
+                    (XftChar8*)text, strlen(text),
+                    &msgshadowcolor,
+                    shadowXOffset, shadowYOffset);
+    XFlush(Dpy);
+    XftDrawDestroy(draw);
+}
+
+
+void Panel::SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font,
+                            int x, int y, XftChar8 *string, int len,
+                            XftColor* shadowColor,
+                            int xOffset, int yOffset)
+{
+    if (xOffset && yOffset) {
+        XftDrawString8(d, shadowColor, font, x+xOffset, y+yOffset,
+                       string, len);
+    }
+    XftDrawString8(d, color, font, x, y, string, len);
+}

+ 130 - 0
panel.h

@@ -0,0 +1,130 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#ifndef _PANEL_H_
+#define _PANEL_H_
+
+#include <X11/Xlib.h>
+#include <X11/xpm.h>
+#include <X11/cursorfont.h>
+#include <X11/Xmu/WinUtil.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <iostream>
+
+#ifdef NEEDS_BASENAME
+#include <libgen.h>
+#endif
+
+#include "switchuser.h"
+#include "input.h"
+#include "const.h"
+#include "image.h"
+
+
+class Panel {
+public:
+    Panel(Display* dpy, int scr, Window root, Cfg* config,
+          const string& themed);
+    ~Panel();
+    void OpenPanel();
+    void ClosePanel();
+    void ClearPanel();
+    void Message(const char* text);
+    void Error(const char* text);
+    Input* GetInput();
+    int EventHandler(XEvent* event);
+    string getSession();
+
+private:
+    Panel();
+    void Cursor(int visible);
+    unsigned long GetColor(const char* colorname);
+    void OnExpose(XEvent* event);
+    void OnKeyPress(XEvent* event);
+    void ShowText();
+    void SwitchSession();
+    void ShowSession();
+
+    void SlimDrawString8(XftDraw* d, XftColor* color, XftFont* font,
+                         int x, int y, XftChar8 *string, int len,
+                         XftColor* shadowColor,
+                         int xOffset, int yOffset);
+
+    Cfg* cfg;
+
+    // Private data
+    Window Win;
+    Window Root;
+    Display* Dpy;
+    int Scr;
+    int X, Y;
+    GC TextGC;
+    XftFont* font;
+    XftColor inputshadowcolor;
+    XftColor inputcolor;
+    XftColor msgcolor;
+    XftColor msgshadowcolor;
+    XftFont* msgfont;
+    XftColor introcolor;
+    XftFont* introfont;
+    XftFont* welcomefont;
+    XftColor welcomecolor;
+    XftColor welcomeshadowcolor;
+    XftFont* enterfont;
+    XftColor entercolor;
+    XftColor entershadowcolor;
+    int Action;
+
+    // Configuration
+    int input_name_x;
+    int input_name_y;
+    int input_pass_x;
+    int input_pass_y;
+    int inputShadowXOffset;
+    int inputShadowYOffset;
+    int input_cursor_height;
+    int welcome_x;
+    int welcome_y;
+    int welcome_shadow_xoffset;
+    int welcome_shadow_yoffset;
+    int intro_x;
+    int intro_y;
+    int username_x;
+    int username_y;
+    int username_shadow_xoffset;
+    int username_shadow_yoffset;
+    int password_x;
+    int password_y;
+    string welcome_message;
+    string intro_message;
+
+    // Pixmap data
+    Pixmap PanelPixmap;
+
+    // Name/Passwd handler
+    Input* In;
+
+    Image* image;
+
+    // For thesting themes
+    bool testing;
+    string themedir;
+
+    // Session handling
+    string session;
+
+};
+
+#endif
+
+

+ 161 - 0
png.c

@@ -0,0 +1,161 @@
+/****************************************************************************
+    png.c - read and write png images using libpng routines.
+    Distributed with Xplanet.
+    Copyright (C) 2002 Hari Nair <hari@alumni.caltech.edu>
+
+    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
+****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <png.h>
+
+int
+read_png(const char *filename, int *width, int *height, unsigned char **rgb, 
+	 unsigned char **alpha)
+{
+    FILE *infile = fopen(filename, "rb");
+
+    png_structp png_ptr;
+    png_infop info_ptr;
+    png_bytepp row_pointers;
+
+    unsigned char *ptr = NULL;
+    png_uint_32 w, h;
+    int bit_depth, color_type, interlace_type;
+    int i;
+
+    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 
+                                     (png_voidp) NULL, 
+                                     (png_error_ptr) NULL, 
+                                     (png_error_ptr) NULL);
+    if (!png_ptr) 
+    {
+        fclose(infile);
+        return(0);
+    }
+  
+    info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr)
+    {
+        png_destroy_read_struct(&png_ptr, (png_infopp) NULL, 
+                                (png_infopp) NULL);
+        fclose(infile);
+        return(0);
+    }
+  
+    if (setjmp(png_ptr->jmpbuf))
+    {
+        png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+        fclose(infile);
+        return(0);
+    }
+  
+    png_init_io(png_ptr, infile);
+    png_read_info(png_ptr, info_ptr);
+
+    png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
+                 &interlace_type, (int *) NULL, (int *) NULL);
+
+    *width = (int) w;
+    *height = (int) h;
+    
+    if (color_type == PNG_COLOR_TYPE_RGB_ALPHA
+	|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    {
+	alpha[0] = malloc(*width * *height);
+	if (alpha[0] == NULL)
+	{
+	    fprintf(stderr, "Can't allocate memory for alpha channel in PNG file.\n");
+	    return(0); 
+	}
+    }
+
+    /* Change a paletted/grayscale image to RGB */
+    if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) 
+        png_set_expand(png_ptr);
+
+    /* Change a grayscale image to RGB */
+    if (color_type == PNG_COLOR_TYPE_GRAY 
+        || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+        png_set_gray_to_rgb(png_ptr);
+
+    /* If the PNG file has 16 bits per channel, strip them down to 8 */
+    if (bit_depth == 16) png_set_strip_16(png_ptr);
+
+    /* use 1 byte per pixel */
+    png_set_packing(png_ptr);
+
+    row_pointers = malloc(*height * sizeof(png_bytep));
+    if (row_pointers == NULL)
+    {
+        fprintf(stderr, "Can't allocate memory for PNG file.\n");
+        return(0);
+    }
+
+    for (i = 0; i < *height; i++)
+    {
+        row_pointers[i] = malloc(4 * *width);
+        if (row_pointers == NULL)
+        {
+            fprintf(stderr, "Can't allocate memory for PNG line.\n");
+            return(0);
+        }
+    }
+
+    png_read_image(png_ptr, row_pointers);
+
+    rgb[0] = malloc(3 * *width * *height);
+    if (rgb[0] == NULL)
+    {
+        fprintf(stderr, "Can't allocate memory for PNG file.\n");
+        return(0);
+    }
+
+    if (alpha[0] == NULL)
+    {
+	ptr = rgb[0];
+	for (i = 0; i < *height; i++)
+	{
+	    memcpy(ptr, row_pointers[i], 3 * *width);
+	    ptr += 3 * *width;
+	}
+    }
+    else
+    {
+	int j;
+	ptr = rgb[0];
+	for (i = 0; i < *height; i++)
+	{
+	    int ipos = 0;
+	    for (j = 0; j < *width; j++)
+	    {
+		*ptr++ = row_pointers[i][ipos++];
+		*ptr++ = row_pointers[i][ipos++];
+		*ptr++ = row_pointers[i][ipos++];
+		alpha[0][i * *width + j] = row_pointers[i][ipos++];
+	    }
+	}
+    }
+
+    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+
+    for (i = 0; i < *height; i++) free(row_pointers[i]);
+    free(row_pointers);
+
+    fclose(infile);
+    return(1);
+}

+ 54 - 0
slim.1

@@ -0,0 +1,54 @@
+." Text automatically generated by txt2man-1.4.7
+.TH slim 1 "May 15, 2005" "" ""
+.SH NAME
+\fBslim \fP- Simple LogIn Manager
+\fB
+.SH SYNOPSIS
+.nf
+.fam C
+\fBslim\fP [\fIoptions\fP] [<arguments>]
+.fam T
+.fi
+.SH DESCRIPTION
+SLiM is a lightweight login manager for X11, allowing the initialization
+of a graphical session by entring username and password in a login screen.
+.SH OPTIONS
+.TP
+.B
+-d
+run as a daemon
+.TP
+.B
+-p /path/to/theme
+display a preview of the theme. An already running X11 session
+is required for theme preview.
+.TP
+.B
+-h
+display a brief help message
+.TP
+.B
+-v
+display version information
+.SH EXAMPLES
+.TP
+.B
+\fBslim\fP -d
+run \fBslim\fP in daemon mode
+.TP
+.B
+\fBslim\fP -p /usr/share/\fBslim\fP/themes/default
+preview of the default theme
+.SH STARTING SLIM AT BOOT
+Please refer to documentation of your Operating System to make \fBslim\fP
+automatically startup after the system boots.
+.SH CONFIGURATION
+Global configuration is stored in the /etc/slim.conf file. See the comments
+inside the file for a detailed explanation of the \fIoptions\fP.
+.SH AUTHORS 
+Simone Rota <sip@varlock.com>
+.PP
+Johannes Winkelmann <jw@tks6.net>
+.SH SEE ALSO
+See the online documentation at the SLiM web site for further information
+on themes, FAQs, etc.

+ 51 - 0
slim.conf

@@ -0,0 +1,51 @@
+# Path, X server and arguments (if needed)
+default_path        ./:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
+default_xserver     /usr/X11R6/bin/X
+#xserver_arguments   -dpi 75
+
+# Commands for halt, login, etc.
+halt_cmd            /sbin/shutdown -h now
+reboot_cmd          /sbin/shutdown -r now
+console_cmd         /usr/X11R6/bin/xterm -C -fg white -bg black +sb -T "Console login" -e /bin/sh -c "/bin/cat /etc/issue; exec /bin/login"
+#suspend_cmd        /usr/sbin/suspend
+
+# This command is executed after a succesful login.
+# you can place the %session and %theme variables
+# to handle launching of specific commands in .xinitrc
+# depending of chosen session and slim theme
+#
+# NOTE: if your system does not have bash you need
+# to adjust the command according to your preferred shell,
+# i.e. for freebsd use:
+# login_cmd           exec /bin/sh - ~/.xinitrc %session
+login_cmd           exec /bin/bash -login ~/.xinitrc %session
+
+# Available sessions (first one is the default).
+# The current chosen session name is replaced in the login_cmd
+# above, so your login command can handle different sessions.
+# see the xinitrc.sample file shipped with slim sources
+sessions            icewm,wmaker,blackbox
+
+# Executed when pressing F11 (requires imagemagick)
+screenshot_cmd      import -window root /slim.png
+
+# welcome message. Available variables: %host, %domain
+welcome_msg         Welcome to %host
+
+# shutdown / reboot messages
+shutdown_msg       The system is halting...
+reboot_msg         The system is rebooting...
+
+# default user, leave blank or remove this line
+# for avoid pre-loading the username.
+#default_user        simone
+
+# current theme, use comma separated list to specify a set to 
+# randomly choose from
+current_theme       default
+
+# Lock file
+lockfile            /var/run/slim.lock
+
+# Log file
+logfile             /var/log/slim.log

+ 101 - 0
switchuser.cpp

@@ -0,0 +1,101 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#include "switchuser.h"
+
+using namespace std;
+
+SwitchUser::SwitchUser(struct passwd *pw, Cfg *c, const string& display)
+    : Pw(pw),
+      cfg(c),
+      displayName(display)
+{
+}
+
+
+SwitchUser::~SwitchUser() {
+    // Never called
+}
+
+
+void SwitchUser::Login(const char* cmd) {
+    SetEnvironment();
+    SetUserId();
+    Execute(cmd);
+}
+
+
+void SwitchUser::SetEnvironment() {
+    char *term = getenv("TERM");
+    char** environ;
+    environ = (char **) new char*[2];
+    environ[0] = 0;
+    if(term)
+        putenv(StrConcat("TERM=", term));
+    putenv(StrConcat("HOME=", Pw->pw_dir));
+    putenv(StrConcat("SHELL=", Pw->pw_shell));
+    putenv(StrConcat("USER=", Pw->pw_name));
+    putenv(StrConcat("LOGNAME=", Pw->pw_name));
+    putenv(StrConcat("PATH=", cfg->getOption("default_path").c_str()));
+    putenv(StrConcat("DISPLAY=", displayName.c_str()));
+    putenv(StrConcat("MAIL="_PATH_MAILDIR"/", Pw->pw_name));
+    chdir(Pw->pw_dir);
+}
+
+
+void SwitchUser::SetUserId() {
+    if( (Pw == 0) ||
+            (initgroups(Pw->pw_name, Pw->pw_gid) != 0) ||
+            (setgid(Pw->pw_gid) != 0) ||
+            (setuid(Pw->pw_uid) != 0) ) {
+        cerr << APPNAME << ": could not switch user id" << endl;
+        exit(ERR_EXIT);
+    }
+}
+
+
+void SwitchUser::Execute(const char* cmd) {
+    char *args[4];
+    char* shell = strdup(Pw->pw_shell);
+    char *shell_basename = BaseName(shell);
+
+    args[0] = new char[strlen(shell_basename) + 2];
+    strcpy(args[0], "-");
+    strcat(args[0], shell_basename);
+    args[1] = "-c";
+    args[2] = (char*)cmd;
+    args[3] = 0;
+
+    execv(shell, args);
+    cerr << APPNAME << ": could not execute login command" << endl;
+}
+
+
+char* SwitchUser::BaseName(const char* name) {
+    const char *base = name;
+
+    while(*name) {
+        if(*name == '/')
+            base = name + 1;
+        ++name;
+    }
+
+    return (char*) base;
+}
+
+
+char* SwitchUser::StrConcat(const char* str1, const char* str2) {
+    char* tmp = new char[strlen(str1) + strlen(str2) + 1];
+    strcpy(tmp, str1);
+    strcat(tmp, str2);
+    return tmp;
+}
+

+ 47 - 0
switchuser.h

@@ -0,0 +1,47 @@
+/* SLiM - Simple Login Manager
+   Copyright (C) 1997, 1998 Per Liden
+   Copyright (C) 2004-05 Simone Rota <sip@varlock.com>
+   Copyright (C) 2004-05 Johannes Winkelmann <jw@tks6.net>
+
+   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.
+*/
+
+#ifndef _SWITCHUSER_H_
+#define _SWITCHUSER_H_
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <paths.h>
+#include <iostream>
+#include "const.h"
+#include "cfg.h"
+
+
+class SwitchUser {
+public:
+    SwitchUser(struct passwd *pw, Cfg *c, const std::string& display);
+    ~SwitchUser();
+    void Login(const char* cmd);
+
+private:
+    SwitchUser();
+    void SetEnvironment();
+    void SetUserId();
+    void Execute(const char* cmd);
+    char* BaseName(const char* name);
+    char* StrConcat(const char* str1, const char* str2);
+    Cfg* cfg;
+    struct passwd *Pw;
+
+    std::string displayName;
+};
+
+
+#endif
+

+ 2 - 0
themes/default/COPYRIGHT.background

@@ -0,0 +1,2 @@
+Text. 04 is copyright (c) 2005 by rafael nascimento
+http://darkevil.deviantart.com

+ 16 - 0
themes/default/COPYRIGHT.panel

@@ -0,0 +1,16 @@
+    Lila SVG Icon and Theme Artwork
+    Copyright (C) 2004 Lila Community
+
+    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

+ 340 - 0
themes/default/LICENSE.panel

@@ -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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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.

BIN
themes/default/background.jpg


BIN
themes/default/panel.png


+ 37 - 0
themes/default/slim.theme

@@ -0,0 +1,37 @@
+# text04 theme for SLiM
+# by Johannes Winkelmann
+
+# Messages (ie: shutdown)
+msg_color               #FFFFFF
+msg_font                Verdana:size=18:bold:dpi=75
+msg_x                   50%
+msg_y                   40%
+msg_shadow_color #702342
+msg_shadow_xoffset 1
+msg_shadow_yoffset 1
+
+# valid values: stretch, tile
+background_style        stretch
+background_color        #eedddd
+
+# Input controls
+input_panel_x           25%
+input_panel_y           65%
+input_name_x            394
+input_name_y            181
+input_font          	Verdana:size=12:dpi=75
+input_fgcolor           #000000
+
+# Username / password request
+username_font          	Verdana:size=14:bold:dpi=75
+username_color        	#f9f9f9
+username_x              280
+username_y              183
+password_x              50%
+password_y              183
+username_shadow_color   #702342
+username_shadow_xoffset 1
+username_shadow_yoffset 1
+
+username_msg            Username:
+password_msg            Password:

+ 16 - 0
xinitrc.sample

@@ -0,0 +1,16 @@
+case $1 in
+icewm)
+	icewmbg &
+	icewmtray &
+	exec icewm
+	;;
+wmaker)
+	exec wmaker
+	;;
+blackbox)
+	exec blackbox
+	;;
+*)
+	exec twm
+	;;
+esac