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 16 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())