Code:
/*-------------------------------------------------------------*/
/* File: xjwmon_2-1.c
*
* Program to view serial output from /dev/ttyS? port.
*/
/*-------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include "xwins.h"
#include "app_glob.c"
#define LEFT_MARGIN 4 /* Leave a 4-pixel margin */
/* Maximum length of string generated by a keypress */
#define MAX_MAPPING 10
/* Menu styles */
#define MENUBAR_STYLE 0
#define PULLDOWN_STYLE 1
#define SCROLL_UP 0
#define SCROLL_DOWN 1
#define MAXCHARS 250 /* Maximum length of a line */
typedef struct D_LINE /* Holds info on each line */
{
struct D_LINE *prev; /* Pointer to previous line */
struct D_LINE *next; /* Pointer to next line */
char *line; /* Null-terminated line */
short length; /* Number of characters in line*/
short lbearing;/* Left and right edges of */
short rbearing;/* rectangle covered by line */
short height; /* Height of text string */
} D_LINE;
typedef struct D_LBUF /* Info on entire buffer */
{
D_LINE *lines; /* First line in buffer */
D_LINE *lstart; /* First line in window */
GC gc; /* GC used to display text */
Region rexp; /* Current exposed region */
XFontStruct *fstruct;/* Info on current font */
int font; /* Currently selected font */
int weight; /* weight and */
int size; /* size */
unsigned dwidth; /* Width of text display area */
unsigned dheight; /* Height of text display area */
long count; /* How many lines in buffer */
} D_LBUF;
static D_LBUF fbuf = { NULL,NULL, None, None, None, 0, 0, 0,
0, 0, 0};
static int zero=0, one=1, two=2, three=3, four=4;
static GC invertGC; /* GC used for highlighting */
static int PullDownOn = 0,
full_update = 0;
static Window pulldown_id;
static unsigned quit_width, quit_height;
/* Information necessary to construct X11R3 font names */
static char *fontname[] =
{
"courier", "helvetica", "new century schoolbook", "times"
};
static char *weight[] = { "medium", "bold" };
static char *size[] = {"100", "120", "140", "180", "240"};
char *quit_label = "Quit";
/* File to be opened */
char *port_name;
static FILE *fp;
/* Function prototypes */
void initapp(/* int, char **, int, int, unsigned, unsigned */);
static int disp_menu(/* caddr_t */);
static int quit_action(/* caddr_t */);
static void process_event();
static void hilite_button(/* XWIN * */);
static int font_select(/* caddr_t */);
static int wt_select(/* caddr_t */);
static int size_select(/* caddr_t */);
static void open_port(/* char *portname */);
static void apply_font();
static void read_lines(); /* read input */
static void display_lines();
static void scroll_lines(/* int direction, int numlines */);
/* Windows in the application */
Window dWin;
XWIN *menubar, *PullDown[3];
/*-------------------------------------------------------------*/
main(argc, argv)
int argc;
char **argv;
{
XWIN *QuitButton, *which_xwin;
XGCValues xgcv;
int i, yp, xp1, xp2;
/* char buf[MAXCHARS] */;
/* FILE *fp; */
xwin_init();
initapp(argc, argv, 20, 20, 500, 400);
/* Get file name from command line */
for (i = 1; i < argc; i += 2)
{
if(strcmp("-port", argv[i]) == 0)
{
port_name = argv[i+1];
break;
}
}
if(i >= argc)
{
fprintf(stderr, "Need: xjwmon -port </dev/ttyS?>\n");
exit(1);
}
/* Create the "Quit" button */
quit_width = XTextWidth(theFontStruct, quit_label,
strlen(quit_label)) + 4;
quit_height = theFontStruct->max_bounds.ascent +
theFontStruct->max_bounds.descent + 4;
QuitButton = MakeXButton(2, 3, quit_width, quit_height,
1, theFGpix, theBGpix, theMain,
quit_label, quit_action, NULL);
/* Create a menu bar */
menubar = MakeXMenu(quit_width+16, 1, 1, theFGpix, theBGpix,
theMain, MENUBAR_STYLE,
"Font", disp_menu, (caddr_t)&zero,
"Weight", disp_menu, (caddr_t)&one,
"Size", disp_menu, (caddr_t)&two,
NULL);
yp = quit_height + 10;
xp1 = quit_width+16 + XTextWidth(theFontStruct,
"Font", strlen("Font")) + 8;
xp2 = xp1 + XTextWidth(theFontStruct,
"Weight", strlen("Weight")) + 8;
/* Create the pull-down menus */
PullDown[0] = MakeXMenu(0, yp, 1, theFGpix, theBGpix,
theMain, PULLDOWN_STYLE,
"Courier", font_select, (caddr_t)&zero,
"Helvetica", font_select, (caddr_t)&one,
"New Century Schoolbook",
font_select, (caddr_t)&two,
"Times", font_select, (caddr_t)&three,
NULL);
PullDown[1] = MakeXMenu(xp1, yp, 1, theFGpix, theBGpix,
theMain, PULLDOWN_STYLE,
"Medium", wt_select, (caddr_t)&zero,
"Bold", wt_select, (caddr_t)&one,
NULL);
PullDown[2] = MakeXMenu(xp2, yp, 1, theFGpix, theBGpix,
theMain, PULLDOWN_STYLE,
" 10pt", size_select, (caddr_t)&zero,
" 12pt", size_select, (caddr_t)&one,
" 14pt", size_select, (caddr_t)&two,
" 18pt", size_select, (caddr_t)&three,
" 24pt", size_select, (caddr_t)&four,
NULL);
/* Set up a GC to display text */
fbuf.gc = XCreateGC(theDisplay, theMain, 0, 0);
XCopyGC(theDisplay, theGC, GCForeground | GCBackground |
GCFont, fbuf.gc);
/* Set up a GC to hilight the buttons */
invertGC = XCreateGC(theDisplay, theMain, 0, 0);
XCopyGC(theDisplay, theGC, GCForeground | GCBackground,
invertGC);
XSetFunction(theDisplay, invertGC, GXinvert);
XSetPlaneMask(theDisplay, invertGC, theFGpix^theBGpix);
/* Add ButtonRelease to the event selection of the main
* window so that we can detect when the user releases
* the button without selecting any menu item.
*/
AddNewEvent(theMain, ButtonReleaseMask|StructureNotifyMask);
/* Create and map a window that serves as the file display area */
dWin = XCreateSimpleWindow(theDisplay, theMain, 1,
quit_height+8, 500, 400, 1, theFGpix, theBGpix);
XSelectInput(theDisplay, dWin, ExposureMask |
ButtonPressMask | OwnerGrabButtonMask|
ButtonReleaseMask | StructureNotifyMask |
KeyPressMask);
XMapRaised(theDisplay, dWin);
/* JW's Notes:
*
*
*/
/* open the serial port using standard I/O. */
open_port(port_name);
read_lines();
fbuf.lstart = fbuf.lines; /* loads .lstart with the first line in the buffer. */
printf("assign fbuf.lstart = fbuf.lines....\n\n");
apply_font();
/* printf("apply_font() completed....\n\n"); */
fbuf.rexp = XCreateRegion();
printf("initialize fbuf.rexp = XCreateRegion() complete....\n\n");
/* Event handling loop--keep processing events until done */
printf("start processing events in event loop....\n\n");
while (!AppDone)
process_event();
/* Close connection to display and exit */
XCloseDisplay(theDisplay);
fclose(fp);
exit(0);
}
/*-------------------------------------------------------------*/
/* p r o c e s s _ e v e n t
*
* Retrieve an event and process it...
*/
static void process_event()
{
XWIN *which_xwin;
/* Get the next event from Xlib */
XNextEvent(theDisplay, &theEvent);
/* If main window is resized, we must resize dWin */
if(theEvent.xany.window == theMain &&
theEvent.type == ConfigureNotify)
{
XResizeWindow(theDisplay, dWin, theEvent.xconfigure.width,
theEvent.xconfigure.height - quit_height - 8);
}
/* Handle events for the dWin, the text display window */
if(theEvent.xany.window == dWin)
{
switch (theEvent.type)
{
case Expose:
{
/* Accumulate exposed areas in a region */
XRectangle r;
r.x = theEvent.xexpose.x;
r.y = theEvent.xexpose.y;
r.width = theEvent.xexpose.width;
r.height = theEvent.xexpose.height;
XUnionRectWithRegion(&r, fbuf.rexp, fbuf.rexp);
if(fbuf.dwidth == 0 || fbuf.dheight == 0)
{
Window rid;
int junk;
/* Get dimensions of this window */
XGetGeometry(theDisplay, theMain, &rid, &junk,
&junk, &fbuf.dwidth, &fbuf.dheight, &junk, &junk);
fbuf.dheight -= (quit_height+8);
}
if(theEvent.xexpose.count == 0) display_lines();
break;
}/* close brace for case Expose: */
case KeyPress:
{
char xlat[MAX_MAPPING];
KeySym key;
int count;
/* Translate keycode into keysym and string */
count = XLookupString(&theEvent, xlat,
MAX_MAPPING, &key, None);
/* Up and Down arrows */
if(key == XK_Up)
scroll_lines(SCROLL_DOWN, 1);
if(key == XK_Down)
scroll_lines(SCROLL_UP, 1);
/* Is it Ctrl-D or Ctrl-U? */
if(theEvent.xkey.state & ControlMask)
{
if(key == XK_l)
/* Refresh display */ XClearArea(theDisplay, dWin,
0,0,0,0,True);
printf("screen refreshed...\n");
if(key == XK_u)
scroll_lines(SCROLL_DOWN,
fbuf.dheight/fbuf.lstart->height-1);
if(key == XK_d)
scroll_lines(SCROLL_UP,
fbuf.dheight/fbuf.lstart->height-1);
} /* close brace for if(theEvent.xkey.state & ControlMask) */
} /* close brace for case: KeyPress */
break;
case ConfigureNotify:
fbuf.dwidth = theEvent.xconfigure.width;
fbuf.dheight = theEvent.xconfigure.height;
XClearArea(theEvent.xany.display, dWin,
0, 0, 0, 0, True);
break;
} /* close brace for switch() */
} /* close brace for if(theEvent.xany.window == dWin) */
/************** Process events for other windows *************/
/* Next, retrieve the XWIN data from Xlib using the
* context manager routine XFindContext
*/
if(XFindContext(theDisplay, theEvent.xany.window,
xwin_context, (caddr_t *) &which_xwin) == 0)
{
/* Call the event handler of this XWIN structure */
if (*(which_xwin->event_handler) != NULL)
(*(which_xwin->event_handler))(which_xwin);
}
/* Handle EnterNotify and LeaveNotify events for the buttons */
if(theEvent.type == EnterNotify ||
theEvent.type == LeaveNotify)
hilite_button(which_xwin);
if(PullDownOn && theEvent.type == ButtonRelease)
PullDownOn = 0;
}
/*-------------------------------------------------------------*/
static int disp_menu(data)
caddr_t data;
{
XWIN *which_xwin;
int menu_number;
/* Display the PullDown menu and process events until a
* choice is made
*/
menu_number = * ((int *) data);
pulldown_id = PullDown[menu_number]->xid;
XMapRaised(theDisplay, pulldown_id);
PullDownOn = 1;
while(PullDownOn)
process_event();
/* Unmap the pull-down menu window */
XUnmapWindow(theDisplay, pulldown_id);
return 1;
}
/*-------------------------------------------------------------*/
/* q u i t _ a c t i o n
*
* This routine is called when a ButtonPress event occurs in
* the quit window.
*/
static int quit_action(data)
caddr_t data;
{
/* Set the done flag */
AppDone = 1;
return 1;
}
/*-------------------------------------------------------------*/
/* h i l i t e _ b u t t o n
*
* Highlight a button window by inverting it
*/
static void hilite_button(p_xwin)
XWIN *p_xwin;
{
int i, go_on = 0;
/* Check if window's parent is one of the pull-down menus */
for(i = 0; i < 3; i++)
{
if(p_xwin->parent == PullDown[i]->xid)
{
go_on = 1;
break;
}
}
if (!go_on) return;
/* Invert a rectangle large enough so that the button window
* gets highlighted
*/
XFillRectangle(theDisplay, p_xwin->xid, invertGC,
0, 0, 1024, 1024);
XFlush(theDisplay);
}
/*-------------------------------------------------------------*/
/* f o n t _ s e l e c t
*
* Process a font selection
*
*/
static int font_select(data)
caddr_t data;
{
int font = *((int *)data);
PullDownOn = 0;
if(font != fbuf.font)
{
fbuf.font = font;
apply_font();
/* Clear the display window so that text is displayed again */
XClearArea(theDisplay, dWin, 0, 0, 0, 0, True);
}
return 1;
}
/*-------------------------------------------------------------*/
/* w t _ s e l e c t
*
* Font weight changed.
*
*/
static int wt_select(data)
caddr_t data;
{
int weight = *((int *)data);
PullDownOn = 0;
if(weight != fbuf.weight)
{
fbuf.weight = weight;
apply_font();
/* Clear the display window so that text is displayed again */
XClearArea(theDisplay, dWin, 0, 0, 0, 0, True);
}
return 1;
}
/*-------------------------------------------------------------*/
/* s i z e _ s e l e c t
*
* New size font selected
*
*/
static int size_select(data)
caddr_t data;
{
int size = *((int *)data);
PullDownOn = 0;
if(size != fbuf.size)
{
fbuf.size = size;
apply_font();
/* Clear the display window so that text is displayed again */
XClearArea(theDisplay, dWin, 0, 0, 0, 0, True);
}
return 1;
}
/*-------------------------------------------------------------*/
/* o p e n s e r i a l p o r t
*
* JW's Notes:
* 1) Open the serial port instead of a file using C libaray
* standard I/O.
*/
static void open_port(portname)
char *portname;
{
/* Open the serial port in "r" (read mode) using standard I/O. */
if((fp = fopen(portname, "r")) == NULL)
{
fprintf(stderr, "Cannot open: %s\n", portname);
exit(1);
}
}
/*-------------------------------------------------------------*/
/* r e a d s e r i a l i n p u t
*
* JW's Notes:
*/
static void read_lines()
{
D_LINE *p_l = NULL, *p_l_prev = NULL;
char buf[MAXCHARS];
/* Read each line and store in linked list of D_LINE structs */
while(((fgets(buf, MAXCHARS, fp)) != NULL) && fbuf.count <= 10)
{
if((p_l = (D_LINE *) calloc(1, sizeof(D_LINE))) != NULL)
{
if(fbuf.lines == NULL)
{
fbuf.lines = p_l;
printf("read the first line fbuf.lines...\n\n");
}
else
{
p_l_prev->next = p_l;
p_l->prev = p_l_prev;
printf("read additional lines... p_l_prev-> = p_l\n\n");
}
}
else
{
/* Allocation failed...*/
fprintf(stderr, "Failed to allocate memory for\
line data structure...\n");
exit(1);
}
p_l->length = strlen(buf) - 1; /* Exclude newline */
buf[p_l->length] = '\0';
if((p_l->line = malloc(p_l->length+1)) == NULL)
{
fprintf(stderr, "Failed to allocate memory for\
line:\n%s\n", buf);
exit(1);
}
strcpy(p_l->line, buf);
p_l_prev = p_l;
fbuf.count++;
printf("This is line %d\n", fbuf.count);
} /* end of while loop */
}
/*-------------------------------------------------------------*/
/* a p p l y _ f o n t
*
* Set up a new font for use. Also compute screen areas
* occupied by each line.
*/
static void apply_font()
{
D_LINE *p_l;
int dir, ascent, descent;
char fname[120]; /* Room for font's name */
XCharStruct cinfo;
int font = fbuf.font,
wt = fbuf.weight,
sz = fbuf.size;
/* Construct font's name */
sprintf(fname, "*adobe-%s-%s-r*%s*", fontname[font],
weight[wt], size[sz]);
printf("Font = %s\n", fname);
/* Free the font, if necessary */
if(fbuf.fstruct != NULL && fbuf.fstruct != theFontStruct)
XFreeFont(theDisplay, fbuf.fstruct);
/* Load the new font */
if ((fbuf.fstruct = XLoadQueryFont(theDisplay, fname))
== NULL)
{
fprintf(stderr, "%s: display %s cannot load font %s\n",
theAppName, DisplayString(theDisplay), fname);
/* Set fbuf.fstruct to our default font */
fbuf.fstruct = theFontStruct;
}
/* Compute bounding box of each line in the buffer */
for(p_l = fbuf.lines; p_l != NULL; p_l = p_l->next)
{
XTextExtents(fbuf.fstruct, p_l->line, p_l->length,
&dir, &ascent, &descent, &cinfo);
p_l->lbearing = cinfo.lbearing;
p_l->rbearing = cinfo.rbearing;
p_l->height = ascent + descent;
}
/* Set the font in the GC */
XSetFont(theDisplay, fbuf.gc, fbuf.fstruct->fid);
}
/*-------------------------------------------------------------*/
/* d i s p l a y _ l i n e s
*
* Displays the lines in the text window
*/
static void display_lines()
{
int xpos = 0;
int ypos = 0, width;
D_LINE *p_l;
/* If a full update is needed, alter the exposed region */
if(full_update)
{
XRectangle r;
r.x = r.y = 0;
r.width = fbuf.dwidth;
r.height = fbuf.dheight;
XUnionRectWithRegion(&r, fbuf.rexp, fbuf.rexp);
full_update = False;
}
/* Set the accumulated exposed regions as the clip mask */
XSetRegion(theDisplay, fbuf.gc, fbuf.rexp);
/* Display the lines starting with the first one. Draw
* a string ONLY if it falls in the exposed region.
*/
for(p_l = fbuf.lstart; p_l != NULL && ypos < fbuf.dheight;
p_l = p_l->next)
{
xpos = LEFT_MARGIN - p_l->lbearing;
ypos += p_l->height;
if(XRectInRegion(fbuf.rexp, xpos, ypos - p_l->height,
p_l->lbearing + p_l->rbearing,
p_l->height) != RectangleOut)
{
/* Yes. String's rectangle is partially in region. Draw it */
XDrawImageString(theDisplay, dWin, fbuf.gc,
xpos, ypos, p_l->line, p_l->length);
}
}
/* Destroy current region and create a new "empty" one */
XDestroyRegion(fbuf.rexp);
fbuf.rexp = XCreateRegion();
/* s c r o l l _ l i n e s
*
* Scroll the text display by a specified amount.
*/
static void scroll_lines(direction, numlines)
int direction;
int numlines;
{
int i, ysrc, yclr, numpix = 0;
D_LINE *p_l;
/* Figure out how many pixels to scroll */
switch(direction)
{
case SCROLL_UP:
for(i = 0, p_l = fbuf.lstart;
i < numlines && p_l->next != NULL;
i++, p_l = p_l->next) numpix += p_l->height;
ysrc = numpix;
yclr = fbuf.dheight - numpix;
break;
case SCROLL_DOWN:
for(i = 0, p_l = fbuf.lstart;
i < numlines && p_l->prev != NULL;
i++, p_l = p_l->prev) numpix += p_l->height;
ysrc = 0;
yclr = 0;
break;
}
if(numpix == 0)
{
/* No need to scroll */
XBell(theDisplay, 0);
return;
}
/* Adjust first line to display */
fbuf.lstart = p_l;
/* Copy area */
XSetClipMask(theDisplay, fbuf.gc, None);
XCopyArea(theDisplay, dWin, dWin, fbuf.gc,
0, ysrc, fbuf.dwidth, fbuf.dheight - numpix,
0, numpix - ysrc);
/* Clear new rectangle so that it is redrawn */
XClearArea(theDisplay, dWin, 0, yclr,
fbuf.dwidth, numpix, True);
}
Jim