Home Page

  About Blind Linux Access

  Project Time Table

  Project Proposal

  First Report

  Second Report

  Final Report

  BlindPenguin Help

  External Links

  Suggestions/Bug Reports

  Download Source Code

BlindPenguin Project

Second Report

penguin.gif


Table of Contents

 

ACKNOWLEDGEMENTS

DECLARATION

1 INTRODUCTION

1.1 What is BlindPenguin?

1.2 Desired Features of BlindPenguin

1.3 Structure of this Document

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 EVALUATION OF BLINDPENGUIN

3.1 Evaluation of Xzoom

3.2 The Addition of a Configuration File

3.3 Signal Processing Functionality

3.4 Mouse Tracking

3.5 Evaluation of BlindPenguin Interface

4 ANALYSIS AND SECOND PROTOTYPE IMPLEMENTATION

4.1 Refinement of First Prototype

4.2 Quick Design

4.2.1 The Configuration File

4.2.2 The Process ID File

4.2.3 Possible File Errors

4.2.4 Why Use Signals for Communications?

4.3 Build Prototype

5 SUMMERY OF WORK DONE

5.1 Modifications Made to Xzoom

5.1.1 The Configuration File

5.1.2 The Process ID File

5.1.3 Mouse Tracking

5.2 Tcl Programming

6 CONCLUSIONS

7 REFERENCES

8 APPENDIX A SOURCE CODE FOR XZOOM

9 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 fulfilment 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: 8th March 2000

Catherine Teahan Date: 8th March 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.

BlindPenguin will consist of three prototypes. Each prototype is accompanied by a report. The first prototype was described in the first report and the second prototype is described in this report. The team are presently finished the second prototype and are now ready to plan the third prototype. The third prototype will be the final version and the team intend that this prototype will contain all the functionality outlined in the first report.

 

1.2 Desired Features of BlindPenguin

The main features that will be incorporated into BlindPenguin when it is finished are:

  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.
  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.
  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.
  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.
  5. Help Facilities - The help feature will guide a user through any aspect of the interface.

 

1.3 Structure of this Document

 

  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.
  2. Evaluation - This evaluates the functionality of the first prototype.
  3. Analysis and Second Prototype Implementation - This section explains how the problems were analysed and how the second prototype was developed.
  4. 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.
  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.
  6. References - A complete list of all the material that the team used to help develop the project.
  7. Appendices - The complete source code for Xzoom and the BlindPenguin Tcl interface.

<< Back toContents


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 Evaluation of BlindPenguin

 

3.1 Evaluation of Xzoom

Following the development of the first prototype of Xzoom an evaluation process was undertaken to determine the success of the prototype. The evaluation was carried out on all the additional functionality, which was added to the original source code.

 

3.2 The Addition of a Configuration File

A configuration file was added to Xzoom to allow users to save settings and access these settings when they ran the program in the future. The configuration file allowed for the saving of size settings only. Before the addition of this capability users had to enter sizes at the command line or edit the source code and recompile Xzoom if they wanted to change the size settings. The configuration file was very successful and there were no implementation errors. If a user did not have the configuration file then the default settings were used, if they did have the file and wished to use command parameters instead of the settings the program was designed to allow them to do so. The only possibility for errors is where a user puts a string in instead of a number which would result in an invalid size but there are error checks in the program which would prevent this from causing serious harm, all that would happen is that the size would not be what the user wanted.

A more fundamental problem with the implementation of the configuration file in the first prototype is that it is stored in the same directory as the Xzoom program which means that any user of the BlindPenguin wishing to save size settings would have to have write access to this directory. This is not a desirable situation and in most cases would be impossible due to security considerations as well as administrative overhead. Apart from the security problems with having a configuration file in the same directory as the program also means that if one user prefers one size and another user prefers another size the configuration file does not perform the function which it should perform as both users would be constantly changing it. Redesigning of the implementation of the configuration file mechanism was necessary.

 

3.3 Signal Processing Functionality

Xzoom was originally designed to function as a stand-alone program. In order for it to work with the Tcl interface it was necessary to give it the capability to process signals. Any signal with the exception of the KILL signal can be caught by a program and used to perform a function. Xzoom was modified to catch the HUP signal because this is the standard UNIX way of telling a process to re-read its configuration file.

The implementation of a signal listener is a standard UNIX practice and there were no implementation problems with this procedure. However it is possible for a program to receive a signal at any time and there is the possibility of a signal being sent to Xzoom before it has drawn its window. This would result in immediate termination of the program but it is unlikely that a signal would be sent to it at this point.

Part of the signal processing functionality involved writing the process ID of Xzoom to a file. When the BlindPenguin Tcl interface needs to send HUP signals to the Xzoom process, it reads the process ID from this file. Like the configuration file the process ID file was stored in the same directory as the Xzoom program this means that if more than one user runs the BlindPenguin the process ID of the previous user will be overwritten preventing the original user from sending HUP signals to it with the Tcl interface. As with the configuration file mechanism the process ID file mechanism has to be redesigned to allow for multiple users.

 

3.4 Mouse Tracking

In order for any meaningful screen navigation a user has to be able to use the mouse and have the screen magnification change relative to the mouse position. The first prototype allows a user to move the mouse within the Xzoom window and review the real screen. This was an improvement on the original program that only allowed screen navigation using the arrow keys. There were however a number of problems with this form of screen navigation:

  1. The mouse had to be located in the BlindPenguin window in order for any tracking to take place.
  2. BlindPenguin often recursively magnified itself.
  3. A user could not click on an application that was magnified because the mouse was in the BlindPenguin window not in the application window.
  4. On leaving the BlindPenguin window all mouse tracking ceased.

 

For these reasons the mouse tracking feature of BlindPenguin had to be completely overhauled and redesigned.

 

3.5 Evaluation of BlindPenguin Interface

A menu bar was successfully implemented which had three menu items: File, Options and Help respectively. When any of these menu items are clicked on a sub menu appears. The only options that were implemented in this prototype were the About option and the Exit option. When the About option is selected a message box appears to inform users who created this system. There is an OK button in this message box which when clicked causes the message box to disappear. When the Exit option is selected the interface terminates.

 

There were a number of features that the interface had to perform but this prototype did not implement these. These features are:

 

  1. Saving configuration files
  2. Sending HUP signals
  3. Reading process ID files

 

<< Back to Contents


4 Analysis and Second Prototype Implementation

 

4.1 Refinement of First Prototype

The results of the Evaluation demonstrated some of the problems with the first prototype. The team decided on the following refinements to be made to the BlindPenguin:

  1. Each user would have their own configuration, which would retain whatever settings they chose to save in it.
  2. Each user would have a process ID file, which would be used by the Tcl interface to send HUP signals to the copy of Xzoom that the user was running.
  3. The Mouse would be tracked no matter where it went on the screen and even if Xzoom was not the active window.

 

4.2 Quick Design

 

4.2.1 The Configuration File

Like many Linux programs the BlindPenguin would have to have a configuration file to allow users to save settings. The problem as identified by the evaluation of the first prototype was that if the BlindPenguin was to support multiple users then it would have to have more than one configuration file, which a user could change to their liking without affecting other users settings. There are a number of ways that such a system could be implemented however it was decided that the most effective would be to place a configuration file in the users home directory. BlindPenguin would look for this file when it started up and the BlindPenguin interface would write to it when a user saved new settings.

The file was named ".BlindPenguin" this is a standard naming scheme for a lot of files which are placed in a users home directory. The '.' means that the file will only be listed when the ls -all command is used and the name itself will instantly inform the user about what the file is.

 

4.2.2 The Process ID File

The problem with the process ID file as discussed in the evaluation is that it can only cater for one user running Xzoom at a time. To allow for multiple users the process ID file had to be implemented in the same way as the configuration file with each user having their own process ID file in their own home directory. The name chosen for the process ID file was ".BlindPenguin.pid" this naming scheme was chosen for the same reasons as the configuration file-naming scheme.

 

4.2.3 Possible File Errors

The most common problem with this type of system is that of a user accidentally deleting their configuration or process ID file. If a user happens to delete the configuration file and they run the Xzoom part of BlindPenguin, Xzoom will simply use the default settings. If a user runs the BlindPenguin interface it will recreate the configuration file when they save their settings.

Deleting a process ID file has no consequences for Xzoom but it will mean that the BlindPenguin interface cannot send HUP signals to Xzoom resulting in an error message to the user. Another problem with the process ID file is that of Xzoom being killed and a user attempting to configure it. In this case the settings will be saved to the configuration file but the user will get an error message saying that the process does not exist. To solve either of these problems all the user has to do is to restart Xzoom.

 

4.2.4 Why Use Signals for Communications?

There are a number of mechanisms which one process can use to talk to another, e.g. pipes, messages and shared memory however the simplest way in which one process can communicate with another process is by sending signals to that other process. Since the BlindPenguin interface does not need to know anything about the Xzoom program sending it a signal is the most appropriate mechanism for communications. The use of signals is only constrained by ownership i.e. a user can only send signals to a process, which they are running. If pipes were to be used then there would have to be a parent child relationship between the Xzoom and the BlindPenguin interface.

 

4.3 Build Prototype

When building the second prototype the team had to write a number of programs for test purposes. These included a simple HUP signal catcher in C that was used to test the BlindPenguin interface to be sure that it was sending HUP signals to the correct process. The program simply printed out a message every time it received a signal. A simple version of the example program in the Xlib Programming Manual (page 82 - 88) was used to test the XtAppTimeOut() and XtAppNextEvent() functions. The source code for xeyes and xwininfo were used for reference purposes.

Implementing the signal handling and the multi user functionality of the second prototype was a relatively simple process. However the mouse tracking functionality was an extremely complicated task. It necessitated the changing of large parts of the Xzoom program and re-writing of the signal handler because if conflicts between it and the timing signals.

It was more difficult to send HUP signals in Tcl because there was limited amount of information available to the team. The Tcl book (Welch, 1997) did not provide any information on signal handling or inter process communication. Learning how to send signals was done by trial and error.

 

<< Back to Contents


5 Summery of Work Done

 

5.1 Modifications Made to Xzoom

The first prototype was an improvement on the original Xzoom program however as discussed in the evaluation section of this document there were a number of design issues that had to be refined.

 

5.1.1 The Configuration File

In the Analysis and Second Prototype Implementation phase it was decided that each user should have their own configuration file stored in their own home directory. The following modification were made to Xzoom to allow this:

 

  1. A Global variable was declared to hold the values of the users HOME directory.
  2. The strcat() function was used to concatenate the HOME directory with the file name.

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.*/

 

RCHomeDir=HomeDir;

strcat(RCHomeDir, "/.BlindPenguin");

strcpy(filename,RCHomeDir);

ConfigFile = fopen(filename,"r");

fscanf(ConfigFile,"%d\n%d", &magx, &magy);

fclose(ConfigFile);

}

 

5.1.2 The Process ID File

In the Analysis and Second Prototype Implementation phase it was decided that each user should have their own process ID stored in their own home directory. The implementation of this was very similar to the implementation of the configuration file solution, some of the same global variables were used. This function is called very early in the life of Xzoom so it was decided to put the call to getenv() here.

 

void CreatPIDFile()

{

/*

This function was written by Kieran O' Sullivan and Catherine Tean It is designed to create a .BlindPenguin.pid file in the home dir of the user running Xzoom. The file will be used by the Tcl interface when sending HUP signals.

*/

char PIDHomeDir[80];

pid = getpid();

 

strcpy (var, "HOME");

HomeDir = getenv(var); /* Get the value for the environment variable HOME */

strcpy(PIDHomeDir,HomeDir);

strcat(PIDHomeDir, "/.BlindPenguin.pid");

strcpy(PIDFileName,PIDHomeDir);

PIDFile = fopen(PIDFileName,"w");

fprintf(PIDFile,"%d\n",pid);

fclose(PIDFile);

}

 

5.1.3 Mouse Tracking

As discussed in the Evaluation phase the mouse tracking provided by the first prototype was not satisfactory for anything more than reviewing the screen. A great many modifications were necessary to Xzoom to allow for the type of mouse tracking discussed in the Analysis and Second Prototype phase:

 

  1. Most of the variables originally declared in main() had to be declared as global so that GetAllEvents could access them.
  2. A timer had to be set using XtAppAddTimeOut() so that Xzoom would run a function GetAllEvents() every 100 mili seconds.
  3. XtAppNextEvent() was added to catch these timer events.
  4. GetAllEvents() had to be written to alter the Xzoom window when the mouse was moved and still handle the keystroke events.
  5. The original event loop had to be moved into the function GetAllEvents().
  6. The signal processing function reset() had to be re-written too because of conflicts with GetAllEvents().
  7. The width and height of the root window and the width and height of the Xzoom window had to be read.
  8. Calculations verification had to be done using the root and Xzoom window coordinates to prevent the generation of invalid window positions.

 

5.1.3.1 Changes Made to Main

int main(int argc, char **argv)

{

 

XtPointer client_data;

.......

signal(SIGHUP, reset); /* This tells the program to run the reset() function when it gets a HUP signal. */

.......

app_context = XtCreateApplicationContext();

 

CreatPIDFile(); /* This function creates a PIDFile which is read by the Tcl interface when it wishes to send a HUP signal to xzoom */

ReadConfig(); /* if there are no command line args then the config file settings will be used. */

 

........

 

root_win = RootWindow(dpy, root_scr);

/* Gets the root window i.e. the window manager the height and width of the window manager is used to prevent the attempts to access areas of the screen that don't exist.*/

 

XtAppAddTimeOut(app_context, (unsigned long) MiliSeconds,

GetAllEvents, client_data);

XtAppNextEvent(app_context, report2);

}

 

5.1.3.2 Changes Made to reset()

void reset(int signum)

{

hup_sig = 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.*/

}

 

5.1.3.3 The GetAllEvents() Function

static void GetAllEvents(XtPointer data, XtIntervalId *id )

{

/* XtPointer data, and XtIntervalId *id are not */

XWindowAttributes win_attributes, root_win_attrib;

Window junkwin;

int junk, AbsUperX, AbsUperY, AbsUperRootX, AbsUperRootY;

unsigned long MiliSeconds;

MiliSeconds = (unsigned long) 100;

tmpx = 0; tmpy = 0;

XQueryPointer (dpy, win, &rep_root, &rep_child,

&rep_rootx, &rep_rooty, &dx, &dy, &rep_mask);

 

/* For efficiancy reasons perform calcualtions only if mous moves */

if ((dx2 != dx) || (dy2 != dy))

{

if (!XGetWindowAttributes(dpy, win, &win_attributes))

{

printf("\nCan't get window attributes.\n");

}

if (XGetWindowAttributes(dpy, root_win,

&root_win_attrib))

{

(void) XTranslateCoordinates (dpy, root_win,

root_win_attrib.root,

-root_win_attrib.border_width,

-root_win_attrib.border_width,

&AbsUperRootX, &AbsUperRootY, &junkwin);

}

(void) XTranslateCoordinates (dpy, win,

win_attributes.root,

-win_attributes.border_width,

-win_attributes.border_width,

&AbsUperX, &AbsUperY, &junkwin);

/*The next set of if statements use the Absolute value of UperX and UperY cordinats to translate the mouse position into an absolute position. The QueryPointer() function only gives a relative position, relative to the window that is using the QueryPointer(). The height and width of the root wincow (window manager) are used to prevent accessing areas of the screen that do not exist. */

 

if ( dx < 0 )

{

xgrab = dx + AbsUperX;

dx2 = dx;

}

 

else

{

tmpx = dx + AbsUperX + 100;

if (tmpx >= root_win_attrib.width )

{

dx2 = dx;

}

else

{

xgrab = dx + AbsUperX;

dx2 = dx;

}

}

if ( dy < 0 )

{

ygrab = dy + AbsUperY;

dy2 = dy;

}

else

{

tmpy = dy + AbsUperY + 65;

if (tmpy >= root_win_attrib.height)

{

dy2 = dy;

}

else

{

ygrab = dy + AbsUperY;

dy2 = dy;

}

}

} /* End if ((dx2 != dx) || (dy2 != dy)) */

if (hup_sig == 1)

{

hup_sig = 0;

ConfigFile = fopen(filename,"r");

fscanf(ConfigFile,"%d\n%d", &magx, &magy);

fclose(ConfigFile);

resize(width[DST], height[DST]);

set_title = True;

signal(SIGHUP, reset); /*This code stops the HUP signal rom being reset to its default behaviour i.e. Termination of the program. Otherwise more than one HUP signal would cause the program to terminate.

 

The code was originally in the reset() function but when the timer was added it had to be moved because of problems with global and local variables */

 

} /* End if (hup_sig == 1) */

XtAppAddTimeOut(app_context,(unsigned long) MiliSeconds,

GetAllEvents, data);

 

/* the rest of this function can be seen in Appendix A */

}

 

5.2 Tcl Programming

 

The BlindPenguin interface was designed in the first prototype so the next stage for the team was to "glue" the interface to the screen magnification system. This required learning how to write to files and send HUP signals. The Tcl programming book (Welch, 1997) proved once again to be very useful in providing good information on file writing. However additional information was obtained on the Internet. The first step here was to allow the user to set their preferred size and to save this size. When the user selects a size in the Set Size option a variable is passed down so that when the user clicks the Save option this procedure reads this variable and writes it to a ".BlindPenguin" file. This ".BlindPenguin" file will now contain the users setting. The ".BlindPenguin" file is saved in the users home directory. This allows for multiple users. The code for writing to files is as follows:

 

set mysize [open $configfile w]

 

puts $mysize $size

puts $mysize $size

 

close $mysize

 

In the above code the $configfile is the file that was concatenated with both the ".BlindPenguin" file and the ".BlindPenguin.pid" file. The $size is the variable that was passed down when the user selected the size.

 

Learning how to send signals was a bit more difficult. The team studied a number of programs that were written that sent signals and from these acquired the knowledge needed to send HUP signals. Sending signals from the BlindPenguin interface to the Xzoom code allows the two to communicate. The programs reads the ".BlindPenguin.pid" file in the users home directory and sends a HUP signal to the process telling it to reread its configuration file. The following illustrates the lines of code needed to send HUP signals:

 

set signal 1

 

set myfile [open $pidfile r]

gets $myfile pids

close $myfile

eval exec [format "kill -%s" $signal] $pids

 

Following the implementation of the Set Size and Save options the next step was to implement the Load Config and Save As options. The team decided that the most appropriate solution would be to create list boxes that would appear when either of these options were selected. These list boxes would contain an entry field and a scrollable list of all the files in the users directory. The entry field allows the user to enter the name of the file that they wish to Load or Save As. Both boxes are the same in each option. The code to create the list box containing the list of files in the users directory is:

 

proc list-box {} {

listbox .fs.files -relief raised -borderwidth 4 \

-yscrollcommand ".fs.scroll set"

pack .fs.files -side left

scrollbar .fs.scroll -command ".fs.files yview"

pack .fs.scroll -side right -fill y

list-out

bind .fs.files <Return> {openentry [.fs.files.entry get]}

}

proc list-out {} {

.fs.files insert end .

.fs.files insert end ..

foreach i [lsort [glob *]] {

.fs.files insert end $i

 

bind .fs.files <Return> {openentry [.fs.file.entry get]}

}

}

 

For this prototype the Load Config is the only one of the two that is implemented. When the user enters the name of the file in the entry field and hits return the contents of that file are copied into the ".BlindPenguin" file. This file will contain the size that the user selected from the Set Size option and saved. If the file name entered does not exist on the list of files that are in the list box an error message is written to the screen telling the user that the file does not exist. The code for accepting the entry in the entry field and copying the contents to the .BlindPenguin file is:

 

proc openentry {entry} {

 

global ent

 

set ent $entry

 

if [file exists $entry] {

 

if [file isdirectory $entry] {

cd $entry

destroy .fs.files

listbox .fs.files -relief raised -borderwidth 3 \

-yscrollcommand ".fs.scroll set"

pack .fs.files -side left

bind .fs.files <Return> {openentry [.fs.file.entry get]}

 

list-out

global entered

 

} elseif [file exists $entry] {

 

if [file isfile $entry] {

 

global env

set myfile $ent

set myfile2 $env(HOME)

 

append myfile2 "/.BlindPenguin"

 

set InFile [open $myfile r]

set OutFile [open $myfile2 w]

while {-1 != [gets $InFile Line]} {

puts $OutFile $Line

 

destroy .fs

}

}}

} else {

puts "file not found"

 

}}

Figure 5.2.1 List Box for Load Config

Load Config List Box

The above is a screen shot of the list box for the Load Config option. The list box for the Save As option is the same except the text Save As replaces the Open File text. However for this prototype the user cannot select any of the files in the list of the list box. They must type the file in the entry field. The team plan to have this option available to the user by the next prototype.

 

<< 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.

The next stage of the project will be the re- evaluation of the second prototype. 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.

 

All the team efforts will be from now on geared towards a final implementation of the objectives set down in the first report for BlindPenguin.

 

<< 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 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>

 

#include <X11/Intrinsic.h> /* for the XtAppContext stuff

Kieran O' Sullivan Catherine Teahan */

 

#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, root_win;

int root_scr; /* Used by BlindPenguin Needed for getting root window */

 

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[80];

char * HomeDir;

char * RCHomeDir;

char var[3];

/* PID File */

char PIDFileName[80];

FILE * PIDFile;

pid_t pid;

 

Window rep_root, rep_child;

XtAppContext app_context;

int rep_rootx, rep_rooty, hup_sig;

unsigned int rep_mask;

int dx, dy;

int dx2, dy2, tmpx, tmpy; /* used to stop un-necessary

pointer queries. */

 

/* and stopped here */

 

/* these were all at the start of main */

XSetWindowAttributes xswa;

int i, j, k;

char c;

char *p1, *p2;

XEvent event;

XEvent * report2;

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;

 

/* they stop 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 reset(int signum);

 

void ReadConfig();

 

static void GetAllEvents ();

 

static void GetAllEvents(XtPointer data, XtIntervalId *id )

{

 

/* XtPointer data, is not used it comes form client_data */

 

XWindowAttributes win_attributes, root_win_attrib;

Window junkwin;

int junk, AbsUperX, AbsUperY, AbsUperRootX, AbsUperRootY;

unsigned long MiliSeconds;

MiliSeconds = (unsigned long) 100;

tmpx = 0;

tmpy = 0;

 

XQueryPointer (dpy, win, &rep_root, &rep_child,

&rep_rootx, &rep_rooty, &dx, &dy, &rep_mask);

 

if ((dx2 != dx) || (dy2 != dy))

{

if (!XGetWindowAttributes(dpy, win, &win_attributes))

{

printf("\nCan't get window attributes.\n");

}

if (XGetWindowAttributes(dpy, root_win, &root_win_attrib))

{

(void) XTranslateCoordinates (dpy, root_win, root_win_attrib.root,

-root_win_attrib.border_width,

-root_win_attrib.border_width,

&AbsUperRootX, &AbsUperRootY, &junkwin);

 

}

(void) XTranslateCoordinates (dpy, win, win_attributes.root,

-win_attributes.border_width,

-win_attributes.border_width,

&AbsUperX, &AbsUperY, &junkwin);

/*

The next set of if statements use the Absolute value of the

UperX and UperY cordinats to translate the mouse position into

an absolute position. The QueryPointer() function only gives a

relitave position, relitave to the window that is using the

QueryPointer(). The height and width of the root wincow

(window manager) are used to prevent accessing areas of the screen

that do not exist.

*/

 

if ( dx < 0 )

{

xgrab = dx + AbsUperX;

dx2 = dx;

}

 

else

{

tmpx = dx + AbsUperX + 100;

if (tmpx >= root_win_attrib.width )

{

dx2 = dx;

}

else

{

xgrab = dx + AbsUperX;

dx2 = dx;

}

}

if ( dy < 0 )

{

ygrab = dy + AbsUperY;

dy2 = dy;

}

else

{

tmpy = dy + AbsUperY + 65;

if (tmpy >= root_win_attrib.height)

{

dy2 = dy;

}

else

{

ygrab = dy + AbsUperY;

dy2 = dy;

}

}

} /* End if ((dx2 != dx) || (dy2 != dy)) */

if (hup_sig == 1)

{

hup_sig = 0;

 

ConfigFile = fopen(filename,"r");

 

fscanf(ConfigFile,"%d\n%d", &magx, &magy);

 

fclose(ConfigFile);

printf("\n I read the config file \n");

printf("\n magx = %d magy = %d \n",magx,magy);

resize(width[DST], height[DST]);

set_title = True;

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.

 

The code was originally in the reset() function but

when the timer was added it had to be moved

because the program was going back to the

GetAllEvents() function before it had a chance to

read the config file. */

 

 

} /* End if (hup_sig == 1) */

XtAppAddTimeOut(app_context, (unsigned long) MiliSeconds, GetAllEvents, data);

 

/* this was also in mane */

 

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(&current_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

*/

 

}

 

 

void ReadConfig(void)

{

 

/*

This function was written by Kieran O' Sullivan and Catherine Tean

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.

*/

 

RCHomeDir=HomeDir;

strcat(RCHomeDir, "/.BlindPenguin");

strcpy(filename,RCHomeDir);

ConfigFile = fopen(filename,"r");

 

fscanf(ConfigFile,"%d\n%d", &magx, &magy);

 

fclose(ConfigFile);

printf("\n I read the config file \n");

printf("\n magx = %d magy = %d \n",magx,magy);

}

 

 

void reset(int signum)

{

hup_sig = 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.

XtRemoveTimeOut (*TimerID); */

 

}

 

void CreatPIDFile()

{

/*

This function was written by Kieran O' Sullivan and Catherine Tean

It is designed to creat a .BlindPenguin.pid file in the hume dir of

the user running xzoom. The file will be used by the Tcl interface

when sending HUP signals.

*/

char PIDHomeDir[80];

pid = getpid();

 

strcpy (var, "HOME");

HomeDir = getenv(var); /* Get the value for the environment variable HOME */

strcpy(PIDHomeDir,HomeDir);

strcat(PIDHomeDir, "/.BlindPenguin.pid");

strcpy(PIDFileName,PIDHomeDir);

PIDFile = fopen(PIDFileName,"w");

fprintf(PIDFile,"%d\n",pid);

fclose(PIDFile);

}

 

 

int main(int argc, char **argv)

{

 

/* Kieran was here */

 

// XtAppContext app_context;

 

XtPointer client_data;

unsigned long MiliSeconds = (unsigned long) 100;

 

/* and stopped here */

 

progname = strrchr(argv[0], '/');

if(progname)

++progname;

else

progname = argv[0];

 

/* Kieran was here */

signal(SIGHUP, reset); /* This tells the program to run the

reset() function when it gets a HUP

signal. */

 

/* and stopped here */

 

app_context = XtCreateApplicationContext(); /* Added by Kieran O' Sullivan */

 

CreatPIDFile(); /* This function creates a PIDFile which is

read by the Tcl interface when it wishes

to send a HUP signal to xzoom */

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 */

root_win = RootWindow(dpy, root_scr); /* Gets the root window i.e. the window manager

the height and width of the window manager is

used to prevent the attempts to access areas of

the screen that don't exist.

*/

/* and finished here */

 

XtAppAddTimeOut(app_context, (unsigned long) MiliSeconds, GetAllEvents, client_data);

 

XtAppNextEvent(app_context, report2);

 

 

}

 

 

<< Back to Contents


9 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 -underline 0 -menu $f.file.m

set m [menu $f.file.m -tearoff 1]

 

menubutton $f.opt -text Options -underline 0 -menu $f.opt.m

set l [menu $f.opt.m -tearoff 1]

 

menubutton $f.help -text Help -underline 0 -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 $size}

$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 $l.l

set m1 [menu $l.l -tearoff 1]

$m1 add check -label "X 2" -variable X2 -command {set_size "2"}

$m1 add check -label "X 3" -variable X3 -command {set_size "3"}

$m1 add check -label "X 4" -variable X4 -command {set_size "4"}

$m1 add check -label "X 5" -variable X5 -command {set_size "5"}

$m1 add check -label "X 6" -variable X6 -command {set_size "6"}

 

$l add cascade -label "Set Colour.." -menu $l.2

set m2 [menu $l.2 -tearoff 1]

$m2 add check -label Red -variable Red -foreground red -command {puts "The colour is set to red"}

$m2 add check -label Blue -variable Blue -foreground blue -command {puts "The colour is set to blue"}

$m2 add check -label Green -variable Green -foreground green -command {puts "The colour is set to green"}

$m2 add check -label White -variable White -foreground white -command {puts "The colour is set to white"}

 

$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

 

proc set_size {var} {

global size

set size $var

}

 

proc savefile {size} {

global env

 

set configfile $env(HOME)

set pidfile $env(HOME)

set signal 1

 

append configfile "/.BlindPenguin"

append pidfile "/.BlindPenguin.pid"

 

set mysize [open $configfile w]

 

puts $mysize $size

puts $mysize $size

 

close $mysize

 

set myfile [open $pidfile r]

gets $myfile pids

close $myfile

eval exec [format "kill -%s" $signal] $pids

 

}

 

proc openentry {entry} {

global ent

set ent $entry

 

if [file exists $entry] {

 

if [file isdirectory $entry] {

cd $entry

destroy .fs.files

listbox .fs.files -relief raised -borderwidth 3 \

-yscrollcommand ".fs.scroll set"

pack .fs.files -side left

# bind .fs.files <Return> {opfile}

 

list-out

global entered

 

} elseif [file exists $entry] {

if [file isfile $entry] {

 

 

global env

set myfile $ent

set myfile2 $env(HOME)

 

append myfile2 "/.BlindPenguin"

 

set InFile [open $myfile r]

set OutFile [open "myfile2" w]

while {-1 != [gets $InFile Line]} {

puts $OutFile $Line

destroy .fs

# bind .fs.files <Return> {openentry [.fs.file.entry get]}

}

}

}

} else {

puts "file not found"

}

}

 

 

 

proc file-entry {type} {

frame .fs.file

label .fs.file.label -text $type

entry .fs.file.entry -width 20 -relief sunken -bd 2 -textvariable entered

pack .fs.file.label .fs.file.entry -side left -padx 1m -pady 2m

global entry

 

bind .fs.file.entry <Return> {openentry [.fs.file.entry get]}

pack .fs.file -side top

}

 

proc list-out {} {

.fs.files insert end .

.fs.files insert end ..

foreach i [lsort [glob *]] {

.fs.files insert end $i

}

}

 

 

proc list-box {} {

listbox .fs.files -relief raised -borderwidth 4 \

-yscrollcommand ".fs.scroll set"

pack .fs.files -side left

scrollbar .fs.scroll -command ".fs.files yview"

pack .fs.scroll -side right -fill y

list-out

bind .fs.files <Return> {openentry $entry}

}

 

proc openfile {} {

global boxflag

set boxflag open

toplevel .fs

wm title .fs "Select File:"

file-entry "Open File:"

button .fs.cancel -text "Cancel" -padx 5 -pady 5 -command {destroy .fs}

pack .fs.cancel -side left -padx 5

button .fs.okk -text "OK" -padx 5 -pady 5 -command {destroy .fs}

pack .fs.okk -side left -padx 5

list-box

}

 

proc saveas {} {

global boxflag

set boxflag savefile

toplevel .fs

wm title .fs "Save File:"

file-entry "Save File:"

button .fs.cancel -text "Cancel" -padx 5 -pady 5 -command {destroy .fs}

pack .fs.cancel -side left -padx 5 -pady 5

button .fs.okk -text "OK" -padx 5 -pady 5 -command {destroy .fs}

pack .fs.okk -side left -padx 5

list-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