BlindPenguin Project
First Report
|
|
Table of Contents
ACKNOWLEDGEMENTS
DECLARATION
1 INTRODUCTION
1.1 WHAT IS
BLINDPENGUIN?
1.2 DESIRED FEATURES OF
BLINDPENGUIN
1.2.1
Management of
Configuration Files
1.2.2 Changing Sizes
1.2.3 Changing Hot Keys
1.2.4 Setting Targets
1.2.5 Help Facilities
1.3 STRUCTURE OF THIS
DOCUMENT
1.3.1 Background
Concepts
1.3.2 Analysis and
First Prototype Implementation
1.3.3 Summary of Work
Done
1.3.4 Discussion
1.3.5 Conclusions
1.3.6 References
1.3.7 Appendices
2 BACKGROUND CONCEPTS
2.1
INTRODUCTION TO X
WINDOWS
2.2 INTRODUCTION TO XLIB
2.3 WHAT IS TCL/TK
2.4 THE HISTORY OF TCL
2.5 THE HISTORY OF TK
3 ANALYSIS AND FIRST
PROTOTYPE IMPLEMENTATION
3.1
REQUIREMENT GATHERING AND
REFINEMENT
3.2 QUICK DESIGN
3.3 BUILD PROTOTYPE
4 SUMMARY OF WORK DONE
4.1 XLIB PROGRAMMING
4.1.1
Modifications Made to
Xzoom
4.2 TCL PROGRAMMING
5 DISCUSSION
5.1 PROTOTYPING
5.1.1
Requirement Gathering and
Refinement
5.1.2 Quick Design
5.1.3 Build Prototype
5.1.4 Evaluation
5.1.5 Refine Prototype
5.1.6 Engineer Package
5.2 BENEFITS OF THE
PROTOTYPING METHODOLOGY
5.3 USES OF PROTOTYPING
6 CONCLUSIONS
7 REFERENCES
8 APPENDICES
8.1
APPENDIX A - SOURCE CODE
FOR XZOOM
8.2 APPENDIX B - SOURCE
CODE FOR BLINDPENGUIN INTERFACE
Acknowledgements
The BlindPenguin project would not have been possible without the
help of a number of individuals and organisations. The team would
like to thank the following for their help.
- Mícheál Ó Foghlú for his
work as Project Supervisor.
- Itai Nahshon as the author of the original Xzoom
program.
- Christophe Tronche for putting the Xlib Manual on the
Web.
- Paul A Farrell for his web site on handling signals.
- WIT Computer Society for the use of their Linux server
snet.
<< Back to Contents
Declaration
This work is submitted in partial fulfillment of the requirements
of the degree, BSc in Commercial Software Development. We declare
that the work submitted is our own except where indicated in the
text.
Kieran O' Sullivan Date:14 th January 2000
Catherine Teahan Date:14 th January 2000
<< Back to Contents
1 Introduction
The growth of Linux over the past few years has seen a large
number of programs being developed for X Windows, which is a GUI
(Graphical User Interface) for Linux. However there are still
relatively few screen magnification programs available for disabled
users of X Windows, those that are available are primitive and not
very user friendly.
1.1 What is BlindPenguin?
The BlindPenguin is a screen magnification package for the X
Windows environment. The idea is to produce an easily configured
package on the same lines as some of the commercial screen
magnification packages available for Windows 9X and NT. However since
disabled access is a right, this program will be distributed free
with the source code under GNU General Public Licence.
BlindPenguin is based on the source code of a program called
Xzoom. This program performs magnification very well but it would be
extremely difficult for a novice to configure. To make it easier for
new users a Tcl interface has been written which allows most of the
configuration to be done through a GUI. In addition some of the
tracking features of Xzoom have been improved to allow better screen
navigation.
1.2 Desired
Features of BlindPenguin
The main features that will be incorporated into BlindPenguin when
it is finished are:
1.2.1 Management of
Configuration Files
A configuration file will control BlindPenguin. This file will
exist in a users home directory. Alterations may be made to this
configuration file through the user interface. When these changes
have been saved a HUP signal will be sent to the Xzoom program that
will cause Xzoom to re-read the configuration file and resize itself
accordingly.
1.2.2 Changing Sizes
The default size may not be the best for a particular user.
BlindPenguin allows users to choose from a list of horizontal and
vertical size settings.
1.2.3 Changing Hot Keys
Hot Keys are keystrokes that can be used for navigation and
switching between sizes. If a user does not like a particular key
combination they have the ability to change it using the interface.
1.2.4 Setting Targets
Targets allow users to have sections of the screen permanently
magnified and displayed. This feature is useful for displaying things
such as on screen clock.
1.2.5 Help Facilities
The help feature will guide a user through any aspect of the
interface.
1.3 Structure of
this Document
1.3.1 Background Concepts
There are a number of concepts that may be new to people reading
this document. These include X Windows, Xlib Programming and Tcl
Programming. This section attempts to explain these concepts in a
manner that most people will understand.
1.3.2 Analysis and
First Prototype Implementation
This section explains the how the problems was analyzed and how
the first prototype was developed.
1.3.3 Summary of Work
Done
The modifications made to Xzoom and the work done for the Tcl
interface are explained here. Source code examples are given to
illustrate this work.
1.3.4 Discussion
A detailed explanation of the prototyping methodology used to
develop BlindPenguin is given here. This includes a description of
all the phases of the methodology and an explanation as to why
prototyping was chosen.
1.3.5 Conclusions
A number of conclusions about the way the project is progressing
and discoveries that the team made while developing BlindPenguin are
discussed here.
1.3.6 References
A complete list of all the material that the team used to help
develop the project.
1.3.7 Appendices
The complete source code for Xzoom and the BlindPenguin Tcl
interface.
<< Back to Contents
2 Background Concepts
2.1 Introduction to X
Windows
In September 1987 Massachusetts Institute of Technology, MIT,
released the first version of X Windows commonly referred to as X11.
X Windows was the designed to provide Graphical User Interaction with
a Unix or Unix like system on workstations. The fact that X11 was
designed for workstations ensured that from the very start networking
would be fundamental to the environment.
Networking is key to the X Window philosophy, all applications,
which are written for this GUI, must be capable of running from any
machine on the network. As well as running in a transparent manner,
i.e. the user should not have to know that they are running on a
network, the X Windows environment must be able to handle issues such
as a slow network, communications failures between the X server and
applications running on it and perhaps most importantly the X Windows
system should be able to provide a user friendly interface through
consistent graphical metaphors.
These metaphors are provided using bitmap technology. In bitmapped
graphics each pixel or dot on the screen corresponds to one or more
bits in memory. A feature of the way that X Windows displays
graphics, is that it can have a number of virtual screens, which
allow a user to have data displayed on their station without it
actually being visible. When they want to see the data they simply
use a keystroke or mouse click to activate the virtual screen that
they are interested in.
Virtual screens as well as all the applications are controlled by
a window manager. The window manager decides where everything is put
and it allows users to launch programs. The manager must also allow
users to resize, move or kill windows as they wish. All this is
covered by a set of rules that have been predefined by the programmer
who wrote the window manager. For the most part programmers and users
do not really have to know these rules as they would be too
complicated for users, and programmers should not depend on having a
particular window manager. A program written for X Windows should run
on any window manager.
2.2 Introduction to Xlib
As part of the X Windows system Xlib was developed to allow the
system to be programmed relatively easily. Xlib is a collection of C
functions. These functions communicate directly with the X server to
allow a program to do whatever it needs to do. The main principle
behind X Windows was portability and this is implemented in all the
Xlib functions.
Applications written using the Xlib functions should compile and
run on any machine that has the libraries installed. One of the
strongest portability features of Xlib is that it uses its own
protocol when sending requests over the network to the X server.
Using this protocol the client generates a request and the server
then performs some action or sends some data. The server may perform
an action and not send back any information. This increases network
performance. An example of information being sent to the server from
a client is when an event occurs such as a window being closed down.
Xlib calls tend to be very low level to increase the efficiency of
programs. It is necessary to develop higher level functions which are
more transparent. The most well known toolkit is Tcl/Tk which was
written for the Tcl language. It allows a programmer to create all
the standard GUI objects using a very small amount of code. These
functions deal with the Xlib low level functions so there is no need
for the programmer to worry about them. This method is used for most
programs but in some cases where efficiency is an issue it is better
to use the low level Xlib function calls.
2.3 What is Tcl/Tk
Tcl stands for ''tool command language'' and is pronounced
''tickle.'' Tcl is actually two things: a language and a library.
First, Tcl is a simple textual language, intended primarily for
issuing commands to interactive programs such as text editors,
debuggers, illustrators, and shells. It has a simple syntax and is
also programmable, so Tcl users can write command procedures to
provide more powerful commands than those in the built-in set.
Second, Tcl is a library package that can be embedded in
application programs. The Tcl library consists of a parser for the
Tcl language, routines to implement the Tcl built-in commands, and
procedures that allow each application to extend Tcl with additional
commands specific to that application. The application program
generates Tcl commands and passes them to the Tcl parser for
execution. When the Tcl library receives commands it parses them into
component fields and executes built-in commands directly. For
commands implemented by the application, Tcl calls back to the
application to execute the commands.
An application program gains several advantages by using Tcl for
its command language. First, Tcl provides a standard syntax: once
users know Tcl, they will be able to issue commands easily to any
Tcl-based application. Second, Tcl provides programmability. All a
Tcl application needs to do is to implement a few
application-specific low-level commands. Tcl provides many utility
commands plus a general programming interface for building up complex
command procedures. By using Tcl, applications need not re-implement
these features. Third, extensions to Tcl, such as the Tk toolkit,
provide mechanisms for communicating between applications by sending
Tcl commands back and forth. The common Tcl language framework makes
it easier for applications to communicate with one another.
Tcl was designed with the philosophy that one should actually use
two or more languages when designing large software systems. One for
manipulating complex internal data structures, or where performance
is key, and another, such as Tcl, for writing smallish scripts that
tie together the other pieces, providing hooks for the user to
extend. For the Tcl script writer, ease of learning, ease of
programming and ease of gluing are more important than performance or
facilities for complex data structures and algorithms.
Tk is an extension to Tcl which provides the programmer with an
interface to the X11 windowing system.
2.4 The History of Tcl
The Tcl scripting language grew out of the work of John Ousterhout
on design tools for integrated circuits at the University of
California at Berkeley in the early 1980's. He had written several
interactive tools for IC design, such as Magic and Crystal. Each tool
needed to have a command language (in those days people tended to
invoke tools by typing commands; graphical user interfaces weren't
yet in widespread use). However, his primary interest was in the
tools, not their command languages. He didn't invest much effort in
the command languages and the languages ended up being weak.
In the fall of 1987, he got the idea of building an embeddable
command language. The idea was to spend extra effort to create a good
interpreted language, and furthermore to build it as a library
package that could be reused in many different applications. The
language interpreter would provide a set of relatively generic
facilities, such as variables, control structures, and procedures.
Each application that used the language would add its own features
into the language as extensions, so that the language could be used
to control the application. The name Tcl (Tool Command Language)
derived from this intended usage.
2.5 The History of Tk
One of his other interests at that time was graphical user
interfaces. As GUIs became more and more popular in the 1980s he had
noticed that the complexity of interactive software was rising
rapidly. The most interesting new developments seemed to require
large projects with enormous investments. As a professor with modest
resources, this worried him.
He concluded that the only hope was to reduce the resource
requirements by building large systems out of reusable components. If
most of the complexity of a system was in the components, it would
take quite a bit of work to develop the components, but this could be
done gradually over time, perhaps by several smaller groups working
together.
He also reasoned that component-based design would not work unless
there was a powerful and flexible mechanism for integrating the
components. These thoughts occurred shortly after he had begun
thinking about Tcl, and it occurred to him that an embeddable command
language such as Tcl might also be useful as a scripting language for
integrating components. He decided to test this theory by creating a
set of GUI components as a Tcl extension and using Tcl to assemble
the components into graphical user interfaces. This extension became
Tk.
<< Back to Contents
3 Analysis and First
Prototype Implementation
3.1 Requirement
Gathering and Refinement
The initial step in this stage involved the researching of all the
requirements that would be needed to complete this project. Many
meetings were held where both team members discussed what they wanted
to do and how they were going to do it. The X Windows system was
researched in dept by the use of books and the internet, especially
in the area of screen magnification. The team found some primitive
versions of screen magnification programs that were written for X
Windows. This provided a starting point for the team and the code
from these programs was studied. The team then decided on how they
could improve these programs to make them more user friendly and
useable. Based on this the requirements for the system are as
following:
· Develop an X Windows screen magnification system which is
user friendly, easy to install and configurable by end users.
· Develop a user friendly interface.
· The user can easily resize the screen to their preferred
settings.
· The user can change the colour of the screen.
· The user can save their personal settings so that they do
not have to change them each time that they load up the system.
· There will be tracking of both the mouse movements and
keystrokes.
· There will be access to online help to using the system.
· This help will be easily understood.
From these requirements the programming languages that would be
used was decided. Tcl/Tk was chosen to program the graphical user
interface and C was chosen to program the screen magnification
system.
3.2 Quick Design
After the requirements were decided upon the next stage for the
team was to draw up a preliminary design of the system to aid in the
building of the prototype. This drawing was designed to meet all the
proposed requirements of the system and to make the graphical user
interface as user friendly and accessible as possible. This design
involves a simple menu bar with three menus, File, Options and Help
respectively. Each of these menus have submenus which appear when
then are clicked on by the mouse.
The File menu contains the following:
- Load Config
- Save Config
- Save As…
- Exit
The Options menu contains the following:
- Set Size
- Set Colour
- Set Target
- Hotkeys
The Help menu contains the following:
From these menus the user can set their personal settings and save
them so that they do not have to change their settings each time that
they load up the system.
3.3 Build Prototype
After the team drew up the initial design of the system they
started work on their first prototype. Using this design, programming
in Tcl/Tk began so as to develop the graphical user interface. This
was a slow process, as no previous knowledge of Tcl/Tk was known by
either of the team members. At the same time programming in X Windows
began. Using the code that was available from other X Windows screen
magnification systems the team was able to elaborate on this and
began changing this code to suit the needs of BlindPenguin. The team
is still working on this prototype and is planning to have it
available in time for the second report.
<< Back to Contents
4 Summary of Work Done
4.1 Xlib Programming
When the team first decided to use Xzoom as a basis for their
project, they realised that they would have to learn how to program
using the Xlib low-level functions as this was the way in which Xzoom
was written. This knowledge was acquired from three main sources, The
Xlib Programmers Manual (Nye, 1990), the web (Tronche, 1997) and
source code from a number of X Windows programs.
4.1.1 Modifications
Made to Xzoom
Xzoom is a small program, which provides an extremely efficient
screen magnification algorithms. However it does not track the mouse
very well and it has a tendency to magnify itself which leads to a
recursive magnification of the screen until all the user can see is a
few pixels. For these reasons it was decided that some modifications
would have to be made to the source code to allow for better tracking
of the pointer.
This was achieved by getting Xzoom to listen for PointerMotion
events. When the pointer moves within the Xzoom window then Xzoom is
aware of its motion. The code to get Xzoom to listen to PointerMotion
events is as follows.
xswa.event_mask =
ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|PointerMotionMask;
Once Xzoom was listening for pointer motion events the next step
was to get it to scroll the screen relative to the pointers movement.
This was achieved by adding another case statement to those already
processed by the event loop. An offset was used to generate a
relative position for the mouse so that Xzoom did not magnify itself.
The code to do this is as follows.
case MotionNotify:
xgrab = event.xmotion.x_root - width[SRC];
ygrab = event.xmotion.y_root; - height[SRC]/8;
XDefineCursor(dpy, win, when_button);
break;
When a user changes the size and saves it to the configuration
file using the BlindPenguin interface, a HUP signal is sent to the
Xzoom telling it to re-read the configuration file. Functions called
ReadConfig() and reset() had to be added to read a configuration file
and handle signals, namely the HUP signal, which is sent by the
BlindPenguin interface. Paul Farrell web site was very helpful with
example C code showing how to catch HUP signals. The code for
ReadConfig() and reset() functions is as follows.
void ReadConfig(void)
{
/*
This function was written by Kieran O' Sullivan and Catherine
Teahan.
It is designed to read the config file when the program starts.
This function is also called by reset() when a HUP signal is
recieved from the Tcl interface to BlindPenguin.
*/
strcpy(filename,".BlindPenguin");
ConfigFile = fopen(filename,"r");
fscanf(ConfigFile,"%d\n%d", &magx, &magy);
fclose(ConfigFile);
}
void reset(int signum)
{
/*
This function was written by Kieran O' Sullivan and Catherine Tean
It is designed to re-read the config file if it recieves a HUP
signal from the Tcl interface to BlindPenguin.
*/
signal(SIGHUP, reset); /* This code stops the HUP signal from
being
reset to its default behaviour i.e.
termination of program. Otherwise more
than one HUP signal would cause the
program to terminate */
ReadConfig();
resize(width[DST], height[DST]);
set_title = True;
}
The ReadConfig() function is called before any command line
arguments are processed. If the user specifies a size at the command
line this will over-write the configuration which was read from file.
For example, if the configuration file set the magnification to four
and the user used the command line to set it to eight then the
magnification would be set to eight. This setting would not be
written to the configuration file.
The program starts to listen to HUP signals just before it enters
the event loop in main(). There would be no point listening for HUP
signals before this point as the program would not have finished
configuring itself.
Appendix A Contains the complete source code of Xzoom with all
the changes clearly marked.
4.2 Tcl Programming
During the initial design of the graphical user interface, the
team found it necessary that a scripting language should be used to
program the interface. In general, scripting languages are easier and
faster to code in than the more structured, compiled languages such
as C and C++. Also scripting languages are sometimes considered good
"glue" languages for tying several programs together. Tcl was chosen
as the scripting language to be used.
Tcl was new to both team members so a learning process began. The
college library had only a few books on the language so the team had
to obtain knowledge from the internet as well. It was difficult to
acquire basic beginners manuals on the internet so the team had to
depend on the a standard Tcl book (Walch , 1997) for their
information.
Tcl was found to be very like the C programming language, but less
complicated. The learning process was slow as it was new to the team.
It was found, however, that it required only one line of code to
create a window. The code to create this window is:
#!/usr/X11/bin/wish
This is one of the many advantages of using Tcl; the programs
require less lines of code than structured language programs.
The main objective for the project for the time being was to
create a menu bar with three different menus: file, options and help
respectively. These menus will allow the user to set their settings
and to save their settings. This interface had to be as user friendly
as possible as this was one of requirements of the system. As a
result this initial interface has been created for BlindPenguin.
Figure 4.1 Interface for BlindPenguin
Above is a screen shot of the menu bar created for BlindPenguin.
Each of the menus have sub menus which appear when clicked on.
Figure 4.2 File Menu
Above is a screen shot of the File menu and all the options
available to the user in this menu. Exit is the only option that is
working at the moment. When Exit is selected the program terminates.
The code for Exit is as follows:
$m add command -label "Exit" -command exit
Figure 4.3 Options Menu
Above is a screen shot of the Options menu. This menu allows the
user to select their preferred settings. The code to create the
Options menu and the user settings is:
menubutton $f.opt -text Options -menu $f.opt.m
set l [menu $f.opt.m -tearoff 1]
$l add cascade -label "Set Size" -menu $m.opt
set m1 [menu $m.opt -tearoff 0]
$l add command -label "Set Colour"
$l add command -label "Set Target"
$l add separator
$l add command -label "Hot Keys"
The arrow after the set size option indicates that it is a
cascaded menu. When this option is selected another submenu will
appear with a number of different size options for the user to choose
from.
Figure 4.4 Help Menu
Above is a screen shot of the Help menu. This is where the user
will acquire any help that they will need when using the system. The
About box contains the name of the system and the team members
involved in the creation of this system. The code for the About box
is as follows:
#The about box
proc about {} {
toplevel .about -borderwidth 3
wm title .about "About BlindPenguin"
wm geometry .about 250x150
message .about.people -width 12c -text "BlindPenguin was created
by:
Catherine Teahan
&
Kieran O'Sullivan"
button .about.ok -text "OK" -command "destroy .about"
pack .about.people
pack .about.ok -padx 3m -pady 3m
}
Appendix B Contains the complete source code of the BlindPenguin
interface.
<< Back to Contents
5 Discussion
5.1 Prototyping
BlindPenguin will be developed using a prototyping methodology.
This methodology is an iterative process involving six stages:
1. Requirement Gathering and Refinement.
2. Quick Design.
3. Build Prototype.
4. Evaluation.
5. Refine Prototype.
6. Engineer Package.
Incremental prototyping approaches build a system in small steps
or components, each of which can:
- work on its own .
- be coded, tested and implemented independently.
5.1.1 Requirement
Gathering and Refinement
This stage involves researching the user needs and also the needs
of the operating system and software been used. This information can
be obtained through numerous discussions with the user and through
the research of books written on the operating system and the
software that is to be used. These requirements will then be modified
or refined to meet the needs of the users.
5.1.2 Quick Design
Using the requirements, a preliminary design of the system will be
drawn up to aid in the building of a prototype.
5.1.3 Build Prototype
After the design of the system is drawn up, development on the
first prototype begins. This prototype does not have to meet all the
specified requirements, as it is only the initial prototype.
5.1.4 Evaluation
In this stage the previous prototype will be tested to see how
well it conforms to the requirements. The results will be evaluated
and necessary changes will be made.
5.1.5 Refine Prototype
Following the evaluation, any modifications or refinements that
need to be made to the prototype will be implemented.
5.1.6 Engineer Package
When the team is satisfied that all requirements have been met and
implemented the final package is delivered.
5.2 Benefits of the
Prototyping Methodology
The project team gains an early understanding of the system and
becomes productive early.
- Tangible progress is delivered quickly.
- Participation by the functional user is well defined and is
part of all project phases.
- The system can be fine-tuned long before the end of the
project.
- Prototype tests act as visible milestones, providing
checkpoints on the project team's progress.
5.3 Uses of Prototyping
- The principle use is to help users and developers understand
the requirements for the system.
- It can be used for back to back testing.
<< Back to Contents
6 Conclusions
At present the project has met all the deadlines that the team
have set and those deadlines set by the college. The use of the
Prototyping methodology has proved very efficient both in terms of
understanding the exact nature of the project and in terms of getting
the work done.
The use of Tcl has also aided the process of prototyping as it has
allowed for the rapid development of a user interface. Originally the
team considered using low-level Xlib functions to achieve this,
however this approach would not have enabled the rapid development of
a user interface.
The next stage of the project will be the Evaluation of the work
done. This stage will most likely result in the development of a more
refined prototype which will incorporate more of the features
mentioned in the introduction.
<< Back to Contents
7 References
Farrell Paul A web site
http://dune.mcs.kent.edu/~farrell/sys95/notes/examples/prog/signal/
This site contains information about Distributed Systems and IPC,
including source code.
Tronche Christophe web site.
http://www.tronche.com/gui/x/
Contains the full Xlib Manual in HTML format.
Nye A 1990.
Xlib Programming Manual
ISBN 0-937175-11-0
O'Reilly & Associates, Inc., Sebastopol, CA 95472
Walch B 1997.
Practical Programming in Tcl and Tk Second Edition.
ISBN 0-L3-6L6830-2
Prentice Hall PTR, Upper Saddle River, NJ 07458.
<< Back to Contents
8 Appendices
8.1 Appendix A -
Source Code for Xzoom
/* This program is distributed with no warranty.
Source files for this program may be distributed freely.
Modifications to this file are okay as long as:
a. This copyright notice and comment are preserved and
left at the top of the file.
b. The man page is fixed to reflect the change.
c. The author of this change adds his name and change
description to the list of changes below.
Executable files may be distributed with sources, or with
exact location where the source code can be obtained.
Changelist:
------ -----------
Itai Nahshon Version 0.1, Nov. 21 1995
Itai Nahshon Version 0.2, Apr. 17 1996
include <sys/types.h>
Use memmove() instead of memcopy()
Optional macro to replace call to usleep().
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#ifdef XSHM
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#endif
#include <X11/cursorfont.h>
#include <X11/keysym.h>
/*
#ifdef TIMER
#include <sys/time.h>
#include <unistd.h>
#endif
*/
Display *dpy;
Screen *scr;
Window win;
GC gc;
/*
#ifdef FRAME
GC framegc;
#endif
*/
/*
#ifdef TIMER
Font font;
struct timeval old_time;
#endif
*/
Cursor when_button;
Cursor crosshair;
char *progname;
int set_title;
#define SRC 0 /* index for source image */
#define DST 1 /* index for dest image */
#define WIDTH 256 /* default width */
#define HEIGHT 256 /* default height */
#define MAG 2 /* default magnification */
#define MAGX MAG /* horizontal magnification */
#define MAGY MAG /* vertical magnification */
int xgrab, ygrab; /* where do we take the picture from */
int magx = MAGX;
int magy = MAGY;
int flipxy = False; /* flip x and y */
int flipx = False; /* flip display about y axis */
int flipy = False; /* flip display about x axiz */
int xzoom_flag = False; /* next mag change only to magx */
int yzoom_flag = False; /* next mag change only to magy */
int width[2] = { 0, WIDTH };
int height[2] = { 0, HEIGHT };
#ifdef XSHM
XShmSegmentInfo shminfo[2]; /* Segment info. */
#endif
XImage *ximage[2]; /* Ximage struct. */
int created_images = False;
#define NDELAYS 5
int delays[NDELAYS] = { 200000, 100000, 50000, 10000, 0 };
int delay_index = 0;
int delay = 200000; /* 0.2 second between updates */
/* Kieran was here */
FILE * ConfigFile;
char filename[13];
/* and stopped here */
void timeout_func(int signum) {
set_title = True;
}
/*
#ifdef FRAME
#define DRAW_FRAME() \
XDrawRectangle(dpy, RootWindowOfScreen(scr), framegc, xgrab,
ygrab, width[SRC]-1, height[SRC]-1)
#endif
*/
void allocate_images(void) {
int i;
#ifndef XSHM
char *data;
#endif
for(i = 0; i < 2; i++) {
#ifdef XSHM
ximage[i] = XShmCreateImage(dpy,
DefaultVisualOfScreen(scr),
DefaultDepthOfScreen(scr),
ZPixmap, NULL, &shminfo[i],
width[i], height[i]);
if(ximage[i] == NULL) {
perror("XShmCreateImage");
exit(-1);
}
shminfo[i].shmid = shmget(IPC_PRIVATE,
(unsigned int)(ximage[i]->bytes_per_line *
ximage[i]->height),
IPC_CREAT | 0777);
if(shminfo[i].shmid < 0) {
perror("shmget");
exit(-1);
}
shminfo[i].shmaddr = (char *)shmat(shminfo[i].shmid, 0, 0);
if (shminfo[i].shmaddr == ((char *) -1)) {
perror("shmat");
exit(-1);
}
#ifdef DEBUG
fprintf(stderr, "new shared memory segment at 0x%08x size %d\n",
shminfo[i].shmaddr, ximage[i]->bytes_per_line *
ximage[i]->height);
#endif
ximage[i]->data = shminfo[i].shmaddr;
shminfo[i].readOnly = False;
XShmAttach(dpy, &shminfo[i]);
XSync(dpy, False);
shmctl(shminfo[i].shmid, IPC_RMID, 0);
#else
data = malloc(width[i] * height[i]);
ximage[i] = XCreateImage(dpy,
DefaultVisualOfScreen(scr),
DefaultDepthOfScreen(scr),
ZPixmap, 0, data,
width[i], height[i], 8, width[i]);
if(ximage[i] == NULL) {
perror("XCreateImage");
exit(-1);
}
#endif XSHM
}
created_images = True;
}
void destroy_images(void) {
int i;
for(i = 0; i < 2; i++) {
#ifdef XSHM
XShmDetach(dpy, &shminfo[i]); /* ask X11 to detach shared
segment */
shmdt(shminfo[i].shmaddr); /* detach it ourselves */
#else
free(ximage[i]->data);
#endif
ximage[i]->data = NULL; /* remove refrence to that address */
XDestroyImage(ximage[i]); /* and destroy image */
}
}
void Usage(void) {
fprintf(stderr, "Usage: %s [ args ]\n"
"Command line args:\n"
"-display displayname\n"
"-mag magnification [ magnification ]\n"
"-geometry geometry\n"
"-source geometry\n"
"-x\n"
"-y\n"
"-xy\n\n"
"Window commands:\n"
"+: Zoom in\n"
"-: Zoom out\n"
"x: Flip right and left\n"
"y: Flip top and bottom\n"
"z: Rotate 90 degrees counter-clockwize\n"
"w: Next '+' or '-' only change width scaling\n"
"h: Next '+' or '-' only change height scaling\n"
"d: Change delay between frames\n"
"q: Quit\n"
"Arrow keys: Scroll in direction of arrow\n"
"Mouse button drag: Set top-left corner of viewed area\n",
progname);
exit(1);
}
/* resize is called with the dest size.
we call it then manification changes or when
actual window size is changed */
void resize(int new_width, int new_height)
{
if(created_images)
destroy_images(); /* we can get rid of these */
/* find new dimensions for source */
if(flipxy) {
height[SRC] = (new_width+magx-1) / magx;
width[SRC] = (new_height+magy-1) / magy;
printf("\nKieran: flibxy if statement in resize() was ture \n");
}
else {
width[SRC] = (new_width+magx-1) / magx;
height[SRC] = (new_height+magy-1) / magy;
}
if(width[SRC] > WidthOfScreen(scr))
width[SRC] = WidthOfScreen(scr);
if(height[SRC] > HeightOfScreen(scr))
height[SRC] = HeightOfScreen(scr);
/* temporary, the dest image may be larger than the
actual window */
if(flipxy) {
width[DST] = magx * height[SRC];
height[DST] = magy * width[SRC];
}
else {
width[DST] = magx * width[SRC];
height[DST] = magy * height[SRC];
}
allocate_images(); /* allocate new images */
/* remember actual window size */
if(width[DST] > new_width)
width[DST] = new_width;
if(height[DST] > new_height)
height[DST] = new_height;
}
void ReadConfig(void)
{
/*
This function was written by Kieran O' Sullivan and Catherine
Teahan. It is designed to read the config file when the program
starts.
This function is also called by reset() when a HUP signal is
recieved from the Tcl interface to BlindPenguin.
*/
strcpy(filename,".BlindPenguin");
ConfigFile = fopen(filename,"r");
fscanf(ConfigFile,"%d\n%d", &magx, &magy);
fclose(ConfigFile);
}
void reset(int signum)
{
/*
This function was written by Kieran O' Sullivan and Catherine
Teahan
It is designed to re-read the config file if it recieves a HUP
signal from the Tcl interface to BlindPenguin.
*/
signal(SIGHUP, reset); /* This code stops the HUP signal from
being
reset to its default behaviour i.e.
termination of program. Otherwise more
than one HUP signal would cause the
program to terminate */
ReadConfig();
resize(width[DST], height[DST]);
set_title = True;
}
int main(int argc, char **argv)
{
XSetWindowAttributes xswa;
int i, j, k;
char c;
char *p1, *p2;
XEvent event;
int buttonpressed = False;
int unmapped = True;
int scroll = 1;
char title[80];
XGCValues gcv;
char *dpyname = NULL;
int source_geom_mask = NoValue,
dest_geom_mask = NoValue,
copy_from_src_mask;
int xpos = 0, ypos = 0;
progname = strrchr(argv[0], '/');
if(progname)
++progname;
else
progname = argv[0];
ReadConfig(); /* if there are no command line args then
the config file settings will be used. */
/* parse command line options */
while(--argc > 0) {
++argv;
if(argv[0][0] == '=') {
dest_geom_mask = XParseGeometry(argv[0],
&xpos, &ypos,
&width[DST], &height[DST]);
continue;
}
if(!strcmp(argv[0], "-mag")) {
++argv; --argc;
magx = argc > 0 ? atoi(argv[0]) : -1;
if(magx <= 0)
Usage();
magy = argc > 1 ? atoi(argv[1]) : -1;
if(magy <= 0)
magy = magx;
else {
++argv; --argc;
}
continue;
}
if(!strcmp(argv[0], "-x")) {
flipx = True;
continue;
}
if(!strcmp(argv[0], "-y")) {
flipy = True;
continue;
}
if(!strcmp(argv[0], "-z") ||
!strcmp(argv[0], "-xy")) {
flipxy = True;
continue;
}
if(!strcmp(argv[0], "-source")) {
++argv; --argc;
if(argc < 1)
Usage();
source_geom_mask = XParseGeometry(argv[0],
&xgrab, &ygrab,
&width[SRC], &height[SRC]);
continue;
}
if(!strcmp(argv[0], "-dest") ||
!strcmp(argv[0], "-geometry")) {
++argv; --argc;
if(argc < 1)
Usage();
dest_geom_mask = XParseGeometry(argv[0],
&xpos, &ypos,
&width[DST], &height[DST]);
continue;
}
if(!strcmp(argv[0], "-d") ||
!strcmp(argv[0], "-display")) {
++argv; --argc;
if(argc < 1)
Usage();
dpyname = argv[0];
continue;
}
if(!strcmp(argv[0], "-delay")) {
++argv; --argc;
if(argc < 1)
Usage();
if(sscanf(argv[0], "%u", &delay) != 1)
Usage();
delay *= 1000;
continue;
}
Usage();
}
/* open connection to X server */
if (!(dpy = XOpenDisplay(dpyname))) {
perror("Cannot open display");
exit(-1);
}
/* Now, see if we have to calculate width[DST] and height[DST]
from the SRC parameters */
copy_from_src_mask = NoValue;
if(source_geom_mask & WidthValue) {
if(flipxy) {
height[DST] = magy * width[SRC];
copy_from_src_mask |= HeightValue;
}
else {
width[DST] = magx * width[SRC];
copy_from_src_mask |= WidthValue;
}
}
if(source_geom_mask & HeightValue) {
if(flipxy) {
width[DST] = magx * height[SRC];
copy_from_src_mask |= WidthValue;
}
else {
height[DST] = magy * height[SRC];
copy_from_src_mask |= HeightValue;
}
}
if(copy_from_src_mask & dest_geom_mask) {
fprintf(stderr, "Conflicting dimensions between source and dest
geometry\n");
Usage();
}
scr = DefaultScreenOfDisplay(dpy);
if(DefaultDepthOfScreen(scr) == 8) {
fprintf(stderr, "%s: can work only with 8 bits/pixel\n",
progname);
exit(1);
}
if(source_geom_mask & XNegative)
xgrab += WidthOfScreen(scr);
if(source_geom_mask & YNegative)
ygrab += HeightOfScreen(scr);
if(dest_geom_mask & XNegative)
xpos += WidthOfScreen(scr);
if(source_geom_mask & YNegative)
ypos += HeightOfScreen(scr);
/* printf("=%dx%d+%d+%d\n", width[DST], height[DST], xpos, ypos);
*/
xswa.event_mask =
ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|PointerMotionMask;
xswa.event_mask |= StructureNotifyMask; /* resize etc.. */
xswa.event_mask |= KeyPressMask|KeyReleaseMask; /* commands */
xswa.background_pixel = BlackPixelOfScreen(scr);
win = XCreateWindow(dpy, RootWindowOfScreen(scr),
xpos, ypos, width[DST], height[DST], 0,
DefaultDepthOfScreen(scr), InputOutput,
DefaultVisualOfScreen(scr),
CWEventMask | CWBackPixel, &xswa);
XChangeProperty(dpy, win, XA_WM_ICON_NAME, XA_STRING, 8,
PropModeReplace,
(unsigned char *)progname, strlen(progname));
/*
XChangeProperty(dpy, win, XA_WM_NAME, XA_STRING, 8,
PropModeReplace,
(unsigned char *)progname, strlen(progname));
*/
set_title = True;
XMapWindow(dpy, win);
gcv.plane_mask = AllPlanes;
gcv.subwindow_mode = IncludeInferiors;
gcv.function = GXcopy;
gcv.foreground = WhitePixelOfScreen(scr);
gcv.background = BlackPixelOfScreen(scr);
gc = XCreateGC(dpy, RootWindowOfScreen(scr),
GCFunction|GCPlaneMask|GCSubwindowMode|GCForeground|GCBackground,
&gcv);
/*
#ifdef FRAME
gcv.foreground = AllPlanes;
gcv.plane_mask = WhitePixelOfScreen(scr)^BlackPixelOfScreen(scr);
gcv.subwindow_mode = IncludeInferiors;
gcv.function = GXxor;
framegc = XCreateGC(dpy, RootWindowOfScreen(scr),
GCFunction|GCPlaneMask|GCSubwindowMode|GCForeground,
&gcv);
#endif
*/
/*
#ifdef TIMER
font = XLoadFont(dpy, "fixed");
#endif
*/
resize(width[DST], height[DST]);
/*
#ifdef FRAME
{
static char bitmap_data[] = { 0 };
static XColor col = { 0 };
Pixmap curs = XCreatePixmapFromBitmapData(dpy,
RootWindowOfScreen(scr), bitmap_data, 1, 1, 0, 0, 1);
when_button = XCreatePixmapCursor(dpy, curs, curs, &col,
&col, 0, 0);
}
#else
when_button = XCreateFontCursor(dpy, XC_ul_angle);
#endif
*/
when_button = XCreateFontCursor(dpy, XC_ul_angle);
crosshair = XCreateFontCursor(dpy, XC_crosshair);
XDefineCursor(dpy, win, crosshair);
/* Kieran was here */
signal(SIGHUP, reset); /* This tells the program to run the
reset() function when it gets a HUP
signal. */
/* and finished here */
for(;;) {
while(unmapped?
(XWindowEvent(dpy, win, (long)-1, &event), 1):
XCheckWindowEvent(dpy, win, (long)-1, &event)) {
switch(event.type) {
case ConfigureNotify:
if(event.xconfigure.width != width[DST] ||
event.xconfigure.height != height[DST]) {
resize(event.xconfigure.width, event.xconfigure.height);
}
break;
case ReparentNotify:
break; /* what do we do with it? */
case MapNotify:
unmapped = False;
break;
case UnmapNotify:
unmapped = True;
break;
case KeyRelease:
switch(XKeycodeToKeysym(dpy, event.xkey.keycode, 0)) {
case XK_Control_L:
case XK_Control_R:
scroll = 1;
break;
}
break;
case KeyPress:
switch(XKeycodeToKeysym(dpy, event.xkey.keycode, 0)) {
case XK_Control_L:
case XK_Control_R:
scroll = 10;
break;
case '+':
case '=':
if(!yzoom_flag) ++magx;
if(!xzoom_flag) ++magy;
xzoom_flag = yzoom_flag = False;
resize(width[DST], height[DST]);
set_title = True;
break;
case '-':
if(!yzoom_flag) --magx;
if(!xzoom_flag) --magy;
xzoom_flag = yzoom_flag = False;
if(magx < 1) magx = 1;
if(magy < 1) magy = 1;
resize(width[DST], height[DST]);
set_title = True;
break;
case XK_Left:
if(flipxy)
if(flipx)
ygrab += scroll;
else
ygrab -= scroll;
else
if(flipx)
xgrab += scroll;
else
xgrab -= scroll;
break;
case XK_Right:
if(flipxy)
if(flipx)
ygrab -= scroll;
else
ygrab += scroll;
else
if(flipx)
xgrab -= scroll;
else
xgrab += scroll;
break;
case XK_Up:
if(flipxy)
if(flipy)
xgrab -= scroll;
else
xgrab += scroll;
else
if(flipy)
ygrab += scroll;
else
ygrab -= scroll;
break;
case XK_Down:
if(flipxy)
if(flipy)
xgrab += scroll;
else
xgrab -= scroll;
else
if(flipy)
ygrab -= scroll;
else
ygrab += scroll;
break;
case 'x':
flipx = !flipx;
set_title = True;
break;
case 'y':
flipy = !flipy;
set_title = True;
break;
case 'z':
if(flipx^flipy^flipxy) {
flipx = !flipx;
flipy = !flipy;
}
flipxy = !flipxy;
resize(width[DST], height[DST]);
set_title = True;
break;
case 'w':
xzoom_flag = True;
yzoom_flag = False;
break;
case 'h':
yzoom_flag = True;
xzoom_flag = False;
break;
case 'd':
if(++delay_index >= NDELAYS)
delay_index = 0;
delay = delays[delay_index];
sprintf(title, "delay = %d ms", delay/1000);
XChangeProperty(dpy, win, XA_WM_NAME, XA_STRING, 8,
PropModeReplace,
(unsigned char *)title, strlen(title));
signal(SIGALRM, timeout_func);
alarm(2);
break;
case 'q':
exit(0);
break;
}
break;
case ButtonPress:
/*
#ifdef FRAME
xgrab = event.xbutton.x_root - width[SRC]/2;
ygrab = event.xbutton.y_root - height[SRC]/2;
#else
xgrab = event.xbutton.x_root;
ygrab = event.xbutton.y_root;
#endif
*/
/*DONT WORRY ABOUT BUTTON PRESSES
xgrab = event.xbutton.x_root;
ygrab = event.xbutton.y_root;
XDefineCursor(dpy, win, when_button);
buttonpressed = True;
*/
break;
case ButtonRelease:
/* DONT WORRY ABOUT BUTTON RELEASES
xgrab = event.xbutton.x_root - width[SRC]/2;
ygrab = event.xbutton.y_root - height[SRC]/2;
XDefineCursor(dpy, win, crosshair);
buttonpressed = False;
*/
break;
case MotionNotify:
/* Kieran was here again */
xgrab = event.xmotion.x_root - width[SRC];
ygrab = event.xmotion.y_root; - height[SRC]/8;
XDefineCursor(dpy, win, when_button);
break;
/* and stopped here */
}
/* trying XShmGetImage when part of the rect is
not on the screen will fail LOUDLY..
we have to veryfy this after anything that may
may modified xgrab or ygrab or the size of
the source ximage */
if(xgrab < 0)
xgrab = 0;
if(xgrab > WidthOfScreen(scr)-width[SRC])
xgrab = WidthOfScreen(scr)-width[SRC];
if(ygrab < 0)
ygrab = 0;
if(ygrab > HeightOfScreen(scr)-height[SRC])
ygrab = HeightOfScreen(scr)-height[SRC];
}
#ifdef XSHM
XShmGetImage(dpy, RootWindowOfScreen(scr), ximage[SRC],
xgrab, ygrab, AllPlanes);
#else
XGetSubImage(dpy, RootWindowOfScreen(scr),
xgrab, ygrab, width[SRC], height[SRC], AllPlanes,
ZPixmap, ximage[SRC], 0, 0);
#endif
/*
#ifdef FRAME
if(buttonpressed) { /* show the frame
DRAW_FRAME();
XSync(dpy, False);
}
#endif
*/
/* copy scaled lines from src to dst */
for(j = flipxy?width[SRC]:height[SRC]; --j >= 0; ) {
/* p1 point to begining of scanline j*magy in DST */
p1 = &ximage[DST]->data[ximage[DST]->xoffset +
j*magy*ximage[DST]->bytes_per_line ];
/* p2 point to begining of scanline j in SRC */
/* if flipy then line height[SRC]-1-j */
p2 = &ximage[SRC]->data[ximage[SRC]->xoffset +
(flipy?(height[SRC]-1-j):j)*ximage[SRC]->bytes_per_line ];
if(flipxy) {
int p2step = ximage[SRC]->bytes_per_line;
p2 = &ximage[SRC]->data[ximage[SRC]->xoffset +
(flipy?j:(width[SRC]-1-j))];
if(flipx) {
p2 += p2step * (height[SRC]-1);
p2step = -p2step;
}
for(i = height[SRC]; --i >= 0;) {
c = *p1++ = *p2;
p2 += p2step;
for(k = magx; --k > 0; )
*p1++ = c;
}
}
else if(flipx) {
p2 += width[SRC];
for(i = width[SRC]; --i >= 0;) {
c = *p1++ = *--p2;
for(k = magx; --k > 0; )
*p1++ = c;
}
}
else {
for(i = width[SRC]; --i >= 0;) {
c = *p1++ = *p2++;
for(k = magx; --k > 0; )
*p1++ = c;
}
}
/* p1 point to begining of scanline j*magy in DST */
p1 = &ximage[DST]->data[ximage[DST]->xoffset +
j*magy*ximage[DST]->bytes_per_line ];
/* p2 points to begining of next line */
p2 = p1 + ximage[DST]->bytes_per_line;
/* duplicate that line as needed */
for(k = magy; --k > 0; ) {
#ifdef BCOPY
bcopy(p1, p2, width[DST]);
#else
memmove(p2, p1, width[DST]);
#endif
p2 += ximage[DST]->bytes_per_line;
}
}
#ifdef XSHM
XShmPutImage(dpy, win, gc, ximage[DST], 0, 0, 0, 0, width[DST],
height[DST], False);
#else
XPutImage(dpy, win, gc, ximage[DST], 0, 0, 0, 0, width[DST],
height[DST]);
#endif
if(set_title) {
if(magx == magy && !flipx && !flipy &&
!flipxy)
sprintf(title, "%s x%d", progname, magx);
else
sprintf(title, "%s X %s%d%s Y %s%d",
progname,
flipx?"-":"", magx,
flipxy?" <=>":";",
flipy?"-":"", magy);
XChangeProperty(dpy, win, XA_WM_NAME, XA_STRING, 8,
PropModeReplace,
(unsigned char *)title, strlen(title));
set_title = False;
}
/*
#ifdef TIMER
{
struct timeval current_time;
double DT;
gettimeofday(¤t_time, NULL);
DT = current_time.tv_sec - old_time.tv_sec;
DT += 1e-6*(current_time.tv_usec - old_time.tv_usec);
sprintf(title, "DT=%6.3f", DT);
XDrawString(dpy, win, gc, 20, 20, title, strlen(title));
old_time = current_time;
}
#endif
*/
XSync(dpy, 0);
#ifdef NO_USLEEP
#define usleep(_t) \
{ \
struct timeval timeout; \
timeout.tv_sec = 0; \
timeout.tv_usec = _t; \
select(0, NULL, NULL, NULL, &timeout); \
}
#endif
if(!buttonpressed && delay > 0)
usleep(delay);
/*
#ifdef FRAME
if(buttonpressed) /* erase the frame
DRAW_FRAME();
#endif
*/
}
}
<< Back to Contents
8.2 Appendix B - Source
Code for BlindPenguin Interface
#!/usr/X11/bin/wish
wm minsize . 300 50
wm title . "BlindPenguin"
set f [frame .menubar]
pack $f -fill x
menubutton $f.file -text File -menu $f.file.m
set m [menu $f.file.m -tearoff 1]
menubutton $f.opt -text Options -menu $f.opt.m
set l [menu $f.opt.m -tearoff 1]
menubutton $f.help -text Help -menu $f.help.m
set p [menu $f.help.m -tearoff 1]
pack $f.file $f.opt $f.help -side left
$m add command -label "Load Config" -command {openfile}
$m add separator
$m add command -label "Save Config" -command savefile
$m add command -label "Save As.." -command {saveas}
$m add separator
$m add command -label "Exit" -command exit
$l add cascade -label "Set Size" -menu $m.opt
set m1 [menu $m.opt -tearoff 0]
$m1 add check -label "X 10" -variable foo -command {puts "foo =
$foo"}
$m1 add check -label "X 20" -variable foo -command {puts "foo =
$foo"}
$m1 add check -label "X 30" -variable foo -command {puts "foo =
$foo"}
$m1 add check -label "X 40" -variable foo -command {puts "foo =
$foo"}
$m1 add check -label "X 50" -variable foo -command {puts "foo =
$foo"}
$l add command -label "Set Colour"
$l add command -label "Set Target"
$l add separator
$l add command -label "Hot Keys"
$p add command -label "Contents"
$p add separator
$p add command -label About -command about
#The about box
proc about {} {
toplevel .about -borderwidth 3
wm title .about "About BlindPenguin"
wm geometry .about 250x150
message .about.people -width 12c -text "BlindPenguin was created
by:
Catherine Teahan
&
Kieran O'Sullivan"
button .about.ok -text "OK" -command "destroy .about"
pack .about.people
pack .about.ok -padx 3m -pady 3m
}
<< Back to Contents
|