[comp.lang.eiffel] X resource managment with source

fcaggian@kepler.com (Frank Caggiano) (11/22/90)

 1) Intro

	 I have enclosed code for eiffel which implements the resource
 mangment concept used in X.  This class will allow the application to
 parse the comand line, get application defaults from a file and use
 resources attached to the server (via xrdb).  It uses the same format
 as X (resource seperator resource: value).  As explained in section 2
below  some modification to the standard graphic classes is required
to make maximum use of this.

	 My configuration is Sun Sparc running 4.0.3c, X11R4 and Eiffel
 2.3.4.  I have not tested this code on any other system. (however I
did start developing this under 2.2 and it did work).

	Also note while this all works here I don't consider it as
finished code. I present it more as a starting point for discussions
concerning eiffel code and codeing practices.  Specifically just how
much modification of the standard ISE distribution is acceptable.
While I would be very hesitant to modify the kernel, support and even
the structures libraries I feel the graphics library is far game. How
are others using the graphics library? 

	As an aside to Mr. Johnson who said that his company would not
allow him to post source for fear of losing 'competitive advantage'.
It seems to me that with a technology as new as this the amount of
information you stand to gain by seeing what others are doing and by
having what you are doing examined by others far outweighs any lost
you would incure.  Of course one could just sit back and examine what
others post without posting but that wouldn't really be cricket, would
it. :-) .


  2) Use

	  In order to make the most use of class RESOURCE it is necesary
  to modifiy a few of the standard ISE graphic classes.  

	  I wanted to accomplish the automatic creation of the resource
  databases at the time is was needed.   The normal procedure in X is to
  first parse the command line to get the display variable if specified,
  open the display and then merge the rest of the resource options into
  one database. 

	  The way I accomplished this was to add `resource' as an
  attribute of DEVICE and to add Resource as a once funtion to DEV_CONST
  (just as Black_color is now).  I then modified the create procedure
  for DEVICE to first call resource.create.  This parses the command
  line and sets up the display name.  I also modified device_imp and the
  x device open in window_x.c to use the display name. After the open
  merge is called on Resource to setup the application database.
	
	By installing it this way it doesn't matter in what order you
access the device or resources.  If you decide not to install it this
way remember that merge must be called after the device is opened (it
needs the x handle of the display). 

	Once all this is accomplised it is possible to get the
  resource values by calls to resource.get("name").  So given an object
  which inherited from DEV_CONST it is possible to set its resources by
  calls such as:

	  if Resource.get(".font") = true then
		   set_font(Resource.value)
	   else
		   set_font(whatever);
	   end;

   Or using the Xt idea lets say something like (for a command button):

	   if Resource.get("*cmdbut.select_color") == true then

			   .... etc

	   end;

	   if Resource.get("*cmdbut.font") == true then

			   .......

	   end;

   3) Open Issues

	   The application porgramer needs to create a  C file with the
   option table array and size (see example at end) run cc -c on the file and
   specify the .o file in the .eiffel file on the EXTERNAL line.  I
   couldn't think of a good way to put this knowladge into eiffel.  If
   anyone has any ideas I'd like ot hear from you. I would like to get
   this into eiffel possibly bypassing ParseCmd altogether and parsing
   the command line internally.

	   If you use this class you shouldn't use the ARGUMENTS
   class in the same system, they will probably conflict.

	   The resource looked up is limited. By this I mean the resource
   name and its class (the x class) are identical.  Xt allows for classes
   and names to be different so that for example you can ask for
   emacs.font and Editors.Fonts.  So if a specific font is not set for
   emacs you get the fonts used by editors. 

	   If anyone makes use of this I would appreciate hearing from
   you. Also if you have nay suggestions or enhancments let me know. 

	I have been specifically vague about how to modify the ISE
classes to hook this in tight. I am not sure of the possible
consequences of telling people how to do this.  The changes are fairly
simple if you have been poking around the graphics library.

		
-----------------------------------------------------------------------
Frank Caggiano                      INTERNET: fcaggian@kepler.com  
Kepler Financial Management, Ltd.       UUCP: ..!uunet!kepler1!fcaggian
100 North Country Rd.                    fax: (516) 751-8678
Setauket, NY 11733                     voice: (516) 689-6300 

-----------------------------------------------------------------------
  --
  -- class RESOURCE 
  -- Managed X options and resources.  
  --
  -- Copyright (c) 1990 Kepler Financial Mgmt. 
  --
  -- Permission is granted to anyone to make or distribute
  -- copies of this software  provided that the
  -- copyright notice and this permission notice are preserved.
  -- Ownership remains with Kepler Financial Mgmt.
  --
  -- Frank Caggiano
  -- Tue Nov  19 13:12:08 EST 1990
  --
  class RESOURCE export

	  value, get, set_xclass_name, other_args, 
	  parse_geometry, width_hint, height_hint, x_hint, y_hint,
	  Xdisplay, Application_name, Xclass_name,
	  set_application_name, set_other_args,
	  merge,

	  create_other_arg_array

  inherit
	  BASIC_ROUT;
	  EXCEPTIONS
  feature
	  --
	  -- The first three should probably go into a CONST file
	  --
	  Env_display: STRING is "DISPLAY";
			  -- For getenv call to get display name.

	  Resource_display: STRING is ".display";
			  -- For resource lookup of display name.

	  Apps_db_dir: STRING is "/usr/lib/X11/app-defaults";
			  -- Directory of application resource files.

	  value: STRING;
			  -- last resource gotten from db with get_resource.

	  Xdisplay: STRING;
			  -- X display string used in Xopen call.

	  Application_name: STRING;
			  -- argv[0] of command minus leading directory pathnames.
			  -- Set by x_parse_command.

	  Xclass_name: STRING;
			  -- X class name for resource managment. First character is
			  -- uppercase.

	  set_xclass_name (xc: STRING) is
			  -- set x class name. Set first character of name to upper case.
		  require
			  a_class: not s.void
		  local
			  a: ASCII;
			  c: CHARACTER;
			  s: STRING;
		  do
			  s := xc.duplicate;
			  a.Create;
			  if s.item_code(1) >= a.Lower_a and s.item_code(1) <= a.Lower_z then
				  c := charconv(s.item_code(1) - 32);
				  s.put(c,1);
			  end;

			  Xclass_name := s;
		  ensure
			  Xclass_name = s
		  end;

	  application_db: INTEGER;
			  -- DB of final merged application resources.

	  other_args: ARRAY[STRING];
			  -- Any command line arguments not parsed by the X routines are
			  -- placed here.
	  Create is
		  -- Read in options from command line. Get display variable for xopen.
		  -- The addresses of the routines are passed so that they will not be
		  -- optimized out if creating a C_PACKAGE.
		  external
			  x_parse_command: INTEGER language "C";
			  x_get_resource: STRING language "C";
			  getenv: INTEGER language "C"
		  local
			  c_ptr: INTEGER;
		  do
			  application_db := x_parse_command(	Current, 
								  @set_application_name,
								  @create_other_arg_array,
								  @set_other_args
							     );

			  set_xclass_name(Application_name.duplicate);
			  if application_db /= 0 then
				  Xdisplay := x_get_resource (	application_db,
								  Application_name.to_c,
								  Xclass_name.to_c, 
								  Resource_display.to_c
							      );
			  end;

			  if Xdisplay.void then
				  c_ptr := getenv(Env_display.to_c);
				  if c_ptr = 0 then
					  raise ("failure to get Xdisplay name");
				  else
					  Xdisplay.Create(1);
					  Xdisplay.from_c(c_ptr);
				  end;
			  end;

		  end;
--
-- If you do not modify the ISE classes to use RESOURCE's. change
-- merge so that it doesn't require the xdevice and change the call to
-- x_merger_db(), remove xdevice. Then see xresource.c 
--
	  merge (xdevice: INTEGER) is
			  -- Merge all resource DB into apllication_db.
		  require
			  xdevice_realized: xdevice /= 0
		  external
			  x_merge_db: INTEGER language "C"
		  do
			  Apps_db_dir.extend('/');
			  if Xclass_name.void then
				  Apps_db_dir.append("Eiffel")
			  else
				  Apps_db_dir.append(Xclass_name);
			  end;

			  application_db := x_merge_db(xdevice, Apps_db_dir.to_c);
		  end;

	  get(resource: STRING): BOOLEAN is
			  -- Get the 'resource' from the application db.
			  -- Return true if found.				
		  require
			  db_open: application_db /= 0;
			  valid_res: not resource.void
		  external 
			  x_get_resource: STRING language "C"
		  do
			  value := x_get_resource( application_db, 
						  Application_name.to_c,
						  Xclass_name.to_c ,
						  resource.to_c
						  );
			  if not value.void then
				  Result := true
			  end;
		  end;

	  parse_geometry(geos: STRING) is
		  require
			  a_geometry: not geos.void
		  external
			  x_parse_geometry language "C"
		  do
				  -- NOT implemented yet
		  end;

	  width_hint, height_hint: INTEGER;
			  -- From resource DB. Requested widht and height.
			  -- set by parse_geometry.

	  x_hint, y_hint: INTEGER;
			  -- From resource DB. Requested x,y position.
			  -- set by parse_geometry.

  --
  -- Private routines for the "C" "X" interface.
  -- These routines are only used by external C code.
  --
	  set_application_name (s: STRING) is
				  -- Used by c routine x_parse_command
		  require
			  an_application: not s.void
		  do
			  Application_name := s
		  ensure
			  Application_name = s
		  end;

	  create_other_arg_array(cnt: INTEGER) is
				  -- Used by C routine x_parse_command.
		  require
			  valid_count: cnt > 0
		  do
			  other_args.Create(1,cnt);
		  ensure
			  not other_args.void;
			  other_args.count = cnt
		  end;

	  set_other_args(arg: STRING; cnt: INTEGER) is
				  -- Used by C routine x_parse_command.
		  require
			  arg_realized: not other_args.void and not arg.void;
			  valid_cnt: cnt > 0 and cnt <= other_args.count
		  do
			  other_args.put(arg, cnt)
		  end;

  end; -- class RESOURCE

 -- Begin xresource.c --

 #ifndef lint
     static char *sccsid = "@(#)xresource,c:1.3	11/17/90";
 #endif
 /*
  -- Copyright (c) 1990 Kepler Financial Mgmt. 
  --
  -- Permission is granted to anyone to make or distribute
  -- copies of this software  provided that the
  -- copyright notice and this permission notice are preserved.
  -- Ownership remains with Kepler Financial Mgmt.
  */

 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/Xresource.h>
 #include "/usr/local/Eiffel/files/_eiffel.h"

 /* 
  * These are defined by the applications programmer in a seperate .o file
	  * and linked it at compile time. (Some day, hopefully they will be put into
  * eiffel).
  */
 extern XrmOptionDescRec opTable[]; /* X option table. command line args to parse */
 extern int opTableEntries;	   /* Number of entries in above */

 static XrmDatabase finalDB, cmdDB; /* Private */

 x_parse_command(current, set_app_name, create_arg_array, set_args)
     OBJPTR current;
     ROUT_PTR set_app_name, create_arg_array, set_args;
 {
     ROUT_PTR tmp;
     OBJPTR obj;
     char **argv_get(), *rindex(), **cp, *s, *args[20];
     int n, cnt;

     n = argc_get();		/* from Eiffel runtime lib; original argc count */
     cp = argv_get();		/* from Eiffel runtime lib; orignal argv array  */

      for(cnt = 0; cnt < n; cnt++) 
	  args[cnt] = *(cp + cnt);

      args[cnt] = 0;

      XrmInitialize();		/* Open X data base routiines */

      XrmParseCommand(&cmdDB, opTable, opTableEntries, args[0], &n, args); /* Xlib call */

      /* 
       * Set the command name of the application ..... 
       */
      if((s = rindex(*cp,'/')) != NULL)   /* argv[0] has pathname, strip it */
	  s++;
      else
	  s = *cp;

      obj = (OBJPTR) eif_create("string",strlen(s) + 1);
      tmp = (ROUT_PTR) eif_rout(obj,"from_c");
      (*tmp) (obj, s);
      (*set_app_name) (current, obj);

      /*
       * Now set up to send back unparsed command line args.
       *
       * Create other_arg array in RESOURCE with a size of 'n' ; then
       * loop through the arguments creating a string object for each, copying in the
       * argument and linking the STRING into the array.
       */
      if (n > 1) {
	  (*create_arg_array) (current, n - 1);

	  for(cnt = 1; cnt < n ; cnt++) {
	      obj = (OBJPTR) eif_create("string", strlen(args[cnt] + 1) );
	      tmp = (ROUT_PTR) eif_rout(obj,"from_c");
	      (*tmp) (obj, args[cnt]);
	      (*set_args) (current, obj, cnt);
	  }
      }
      return(1);
  }
  /*
   * Get the requested 'resource' for application 'name' and 'class' from the X database 'db'.
   *
   * Return a string object containing the resource or VOID object if not found.
   */
  OBJPTR x_get_resource(db, name, class, resource)
      int db;
      char *name, *class, *resource;
  {
      XrmValue value;
      char *str_type[20], hold[80];
      char _name[50], _class[50];
      OBJPTR obj;
      ROUT_PTR tmp;

      strcpy(_name,name);		/* for db access */
      strcat(_name,resource);

      strcpy(_class,class);
      strcat(_class,resource);

      obj = 0;

      if(db == 1)
	  db = XrmGetResource(cmdDB, _name, _class, str_type, &value);
      else
	  db = XrmGetResource(finalDB, _name, _class, str_type, &value);

      if(db == True) {
	  strncpy(hold,value.addr,(int)value.size);
	  hold[(int)value.size] = '\0';
	  obj = (OBJPTR) eif_create("string",(int)value.size + 1);
	  tmp = (ROUT_PTR) eif_rout(obj,"from_c");
	  (*tmp) (obj, hold);
      }

      return(obj);
  }

  /*
   * Merge all resource databases into on application database.
   * Return the final database.
   *
   *  For now we merge in: (in this order)
   * 1) the application file 'appsFile'.
   * 2) The resources in the server 'xdevice'.
   * 3) THe command line arguments.
   * 
   * The users .Xdefault file is not currentlly done.
   */
#ifdef notmodified
/*
 * If you chose not to modify the ISE graphic classes to use resource
 * this should work.  It is untested in the non-modified position.
 */
extern Display *X_display;

x_merge_db(appsFile)

#else

  x_merge_db(X_display, appsFile)
      Display *X_display;

#endif
      char *appsFile;
  {
      XrmDatabase fDB, ffDB, XrmGetFileDatabase(), XrmGetStringDatadase();

      if ((fDB = XrmGetFileDatabase (appsFile)) != NULL)
	  XrmMergeDatabases(fDB,&finalDB);

      if(X_display->xdefaults != NULL)  {
	  if((ffDB = XrmGetStringDatabase(xdevice->xdefaults)) != NULL) 
	      XrmMergeDatabases(ffDB,&finalDB);
      }

      XrmMergeDatabases(cmdDB, &finalDB);

      return (2);
  }

  x_parse_geometry(obj, s)
      OBJPTR obj;
      char *s;
  {
  }

 -- Sample command line options file --

 /*
  * aplication agument table
  */

 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/Xresource.h>


 int opTableEntries = 10;
 XrmOptionDescRec opTable[] = {
 { "-bg",	 	"*background",		XrmoptionSepArg,	(caddr_t) NULL},
 { "-bitmap",		".bitmap",		XrmoptionSepArg,	(caddr_t) NULL},
 { "-display",		".display",		XrmoptionSepArg, 	(caddr_t) NULL},
 { "-fg",		"*foreground",		XrmoptionSepArg,	(caddr_t) NULL},
 { "-fn",		"*font",		XrmoptionSepArg,	(caddr_t) NULL},
 { "-m",		".mon",			XrmoptionSepArg,	(caddr_t) NULL},
 { "-y",		".yr",			XrmoptionSepArg,	(caddr_t) NULL},
 { "-tri", 		".tri*background",	XrmoptionSepArg,	(caddr_t) NULL},
 { "-sq",		".sq.background",	XrmoptionSepArg,	(caddr_t) NULL},
 { "=",			"*geometry",		XrmoptionIsArg,		(caddr_t) NULL},
 };

-- 
Frank Caggiano                      INTERNET: fcaggian@kepler.com  
Kepler Financial Management, Ltd.       UUCP: ..!uunet!kepler1!fcaggian
100 North Country Rd.                    fax: (516) 751-8678
Sekauket, NY 11733                     voice: (516) 689-6300 

fcaggian@kepler.com (Frank Caggiano) (11/29/90)

	There is a bug in xresource.c which prevents the command line
from being parsed if a program is run using a leading path. To correct
this problem do:

	In xresource.c move the call to XrmParseCommand() to AFTER the
test of argv[0] for a leading path. 

	In the call to XrmParseCommand(),  replace argv[0] with s;

The code should look like this:

    XrmInitialize();		/* Open X data base routiines */

    /* 
     * Set the command name of the application ..... 
     */
    if((s = rindex(*cp,'/')) != NULL)   /* argv[0] has pathname, strip it */
	s++;
    else
	s = *cp;

    XrmParseCommand(&cmdDB, opTable, opTableEntries, s, &n, args); /* Xlib call */


Recompile and relink.

			Frank Caggiano

-- 
Frank Caggiano                      INTERNET: fcaggian@kepler.com  
Kepler Financial Management, Ltd.       UUCP: ..!uunet!kepler1!fcaggian
100 North Country Rd.                    fax: (516) 751-8678
Sekauket, NY 11733                     voice: (516) 689-6300 

fcaggian@kepler.com (Frank Caggiano) (11/29/90)

	I removed the geometry parsing from resource.e and xresource.c
and made it a seperate class.

	Not sure if sending the address of the integers to
XParseGeometry using the '@' operator is considered ok. It works here.
Anyone have any comments?

This is useable without modification to any of the standard ISE
classes. Setting  x and y in the root window before the first display
will position the window on the screen.  Also the screen size is
avaliable in Device.width and Device.height to allow for relative
positioning from the right or bottom edge of the screen.

If you use this remove parse_geometry from resource.e.

--
-- class GEOMETRY
-- class for parsing the X geometry string.
--
-- Copyright (c) Kepler Financial Mgmt.
--
-- Frank Caggiano
-- Tue Nov  6 13:12:08 EST 1990
--
class GEOMETRY export

	parse_geometry, width_hint, height_hint, x_hint, y_hint,

feature

	parse_geometry(geos: STRING) is
			-- parse X geometry string in the form:
			-- =<width>x<height>{+-}<xoffset>{+-}<yoffset>
		require
			a_geometry: not geos.void
		external
			x_parse_geometry:INTEGER  name "XParseGeometry" language "C"
		local
			x: INTEGER
		do
			x := x_parse_geometry(geos.to_c, @x_hint, @y_hint, 
							 @width_hint, @height_hint);
		end; -- Create

	width_hint, height_hint: INTEGER;
			-- From parsed string, width and height values. 

	x_hint, y_hint: INTEGER;
			-- From parse stringd, x,y screen position.
end; -- GEOMETRY
-- 
Frank Caggiano                      INTERNET: fcaggian@kepler.com  
Kepler Financial Management, Ltd.       UUCP: ..!uunet!kepler1!fcaggian
100 North Country Rd.                    fax: (516) 751-8678
Sekauket, NY 11733                     voice: (516) 689-6300