4.4. Common Graphics and the Integrated Development Environment [Windows only]

Q 4.4-1) Do I need the project system? I already have a way of organizing (eg, using tools such as defsystem) my source files for compiling and loading.  Is there any reason for me to switch to using the project system?
Q 4.4-2) Why can't I create Common Graphics windows from my *common-lisp* buffer in Emacs?
Q 4.4-3) How do I not save prefs.cl when I exit the ide?
Q 4.4-4) Sometimes when I run my project, I get mysterious redefinition warnings. Why and should I worry about them?
Q 4.4-5) I have developed my application in the IDE, and when I test it there it runs fine. But when I try to run the application as generated by File | Build Project Distribution, it starts and the application window appears, but it then immediately exits.

Go to main FAQ page.


Q 4.4-1) Do I need the project system? I already have a way of organizing (eg, using tools such as defsystem) my source files for compiling and loading. Is there any reason for me to switch to using the project system?

A 4.4-1) The project system (first introduced in Allegro CL 5.0 on Windows) was introduced as a way of helping users organize their files as they develop new applications with the Allegro CL 5.0/5.0.1 Interface Builder.  The "File -> Build Project EXE" and "File -> Build Project Distribution" menu commands also use the project system by taking user defined projects and generating from them calls to the Allegro CL functions build-lisp-image and generate-application respectively.

Since the project system is primarily oriented towards Allegro CL 5.0/5.0.1 Interface Builder applications, you may find you don't need to use the project system if you are porting an existing application, with its own way to load/compile source files, to Allegro CL 5.0/5.0.1.

Note that for Allegro CL 5.0/5.0.1 it is necessary to include runtime Common Graphics functionality in all applications built with the project system.  Therefore, if you are not using Common Graphics, you can save space by not using the project system.

If you have been developing your Common Graphics application independently of the project system and would like to use the project system to generate standalone and deliverable versions, you can simply follow these steps to create a project encapsulating your application:

(1) Create a new project using the "File -> New Project" menu item.

(2) Bring up the Project Manager using the "View -> Project Manager" menu item.

(3) Delete the default form by clicking on the "formN" line in the "General" tab of the Project Manager and then clicking on the "delete" button (second from the left -- looks like an "X") of the Project Manager.

(4) Use the "add" button (first from the left -- looks like a "+") of the Project Manager to add as many source files as you wish to the project.  Note that you may choose simply to add a single file which when loaded loads the rest of your application.

(5) In the "Options" tab of the Project Manager, you may wish to change the name, and initialization function for your project.  In the "Advanced" tab, you may wish to deselect (if you don't need them) all of the "Build Includes" items >> except "cg" (which must always be included) << to suppress these Common Graphics modules from being loaded into your standalone.

To save the project, use "File -> Save All".


Q 4.4-2) Why can't I create Common Graphics windows from my *common-lisp* buffer in Emacs?

A 4.4-2) In order to handle events properly, all actions having to do with windows (creating them, displaying them, etc.) must take place within a single (Lisp) process. (On Windows, a Lisp process corresponds to an Operating System thread.) That process is the IDE/Common Graphics process and all interaction with windows (such as the debug window and editor windows) while Allegro CL is running is done within that process. The Emacs-Lisp interface, however, runs in another process. So an action such as creating a window (by, say, calling cg:make-window) cannot be done in Emacs since it would then occur in the incorrect process. You can edit common graphics code in emacs but to evaluate it, you cannot use emacs-lisp interface tools such as C-M-x. Instead, you must save the buffer to a file and load that file into Lisp using tools like the File | Load menu command.


Q 4.4-3) How do I not save prefs.cl when I exit the ide?

A 4.4-3) The idea of writing prefs.cl each time is to "remember" where you last placed your windows (as well as other settings) so that they will be "where you left them" the next time you start up.

If you do not want this behavior, as soon as you start up, toggle Tools | Save Options on Exit to off (unchecked). Then click Tools | Save Options Now. That will write a prefs.cl file with saving prefs.cl on exit disabled (meaning you do not have to toggle it off the next time you use the IDE).


Q 4.4-4) Sometimes when I run my project, I get mysterious redefinition warnings. Why and should I worry about them?

A 4.4-4) The warning likely looks like this:

Warning: FORM1-BUTTON4-ON-CHANGE, :OPERATOR was defined in 
EDITOR 1.unsaved and is now being defined in C:\Program Files\acl501\form1.cl

This warning occurred after placing a button on a form, displaying the Events tab of the Inspector for the button, and clicking on the extended editor button for the on-change event handler. (There is nothing special about buttons. The same behavior would be seen with any control with event handlers.) That generated code for the function FORM1-BUTTON4-ON-CHANGE, which you see in the form1 editor buffer. However, for system reasons it is first stored in a hidden buffer and evaluated (to ensure that the function is defined). When the definition is later read from the form1 buffer, a redefinition warning is signaled.

This is a minor design flaw. You will not get the warning if you display the form1 editor buffer before clicking on the on-change extended editor button. You do this by displaying the Project Manager, View | Project Manager, selecting form1 in the pane, and clicking on the View Selected Code button. Those actions cause a form1 buffer to be displayed in the Editor Workbook, making storing and evaluating the FORM1-BUTTON4-ON-CHANGE code in a hidden buffer unnecessary.


Q 4.4-5) I have developed my application in the IDE, and when I test it there it runs fine. But when I try to run the application as generated by File | Build Project Distribution, it starts and the application window appears, but it then immediately exits.

A 4.4-5) Since the question is asked, we assume that exiting immediately is not what is intended. There are programs that run in batch mode and exit when they are done, with no indication to users. However, other programs display windows and the windows stick around until the user explicitly exits the program. The question above typically arises in these cases. It runs fine when tested with Run | Run Project in the IDE but the application created by File | Build Project Distribution starts up and immediately exits without user action of any sort.

The most likely cause of such behavior is that your project initialization function is not returning your application window, but is instead returning something that is either not a window or is a temporary window. The project initialization function is the value of the on-initialization event handler for your project. It appears in the Init Function field of the Options Tab of the Project manager dialog (displayed with View | Project Manager).

A project application is generated with the menu commands File | Build Project Exe (which produces a .exe file and a .dxl file) or File | Build Project Distribution (which also produces an exe and a dxl file, and puts them along with other files in a directory).

When the application starts up, at the end of the startup procedure, it calls cg::do-default-restart (which is the value of excl:*restart-init-function*). The source for that function looks like:

(defun do-default-restart (&key (console-state SW_HIDE)) 
  (initialize-session) 
  ;; use SW_NORMAL to expose 
  (ShowWindow (console-hwnd (app *system*)) console-state) 
  (let ((main-window (do-initialization))) 
    (event-loop main-window))) 

It calls (do-initialization), which is the project initialization function, takes the return value and passes it to event-loop, whose source looks like:

(defun event-loop (&optional main-window) 
  (unless qmsg (setq qmsg (callocate msg))) 
    (do () 
        ((or (not (streamp main-window)) 
             (closed-stream-p main-window)) 
         (exit 0)) 
      (process-single-event nil))) 

event-loop is supposed to be looping until main-window, its argument, is closed (assuming main-window is a stream of some sort). main-window, again, is the value returned by the project initialization function. If that return value is not a window, then the event-loop falls through immediately and exit is called.

You can fix the problem by having your project initialization function return your main window. The main window, recall, is the window which an application user closes when they want to exit the application.

The default-init-function, which is the initial project initialization function, creates and returns the window associated with the project main form. While it suffices for many application, many application developers write their own project initialization function because they want more than one window created, or a preliminary window (to verify a license or to set configuration options) to appear prior to the main window, or to perform other initializations not conveniently done elsewhere. If this function returns, it must return the main window after doing whatever else it does.

Note that running a project within the IDE does call the project initialization function but does not embed it in an event loop as detailed above (the standard IDE event loop is used instead). Therefore, the exiting behavior of an application will not be seen when testing a project application within the IDE.

Note we said "if this function [the project init function] returns, it must ..." above. The init function does not have to return or it can (more or less equivalently) it can return nil when complete since returning nil causes the application to exit. Instead, it can run its own event loop and handle exiting entirely on its own. Thus, here is an outline of a project init function that does not return until the main application window is closed, at which point it returns nil.To make this work, of course, things surrounded by angle brackets (< and >) or brackets ([ and ]) must be replaced by actual Lisp code doing what is indicated:

(defun my-init-function () 
  <initialize things here> 
  (let* ((my-main-window [find my main window])) 
    (loop ;; keep going after handling an error 
      (handler-case 
        (loop (process-single-event) 
          (when (closed-stream-p my-main-window) 
            (return-from my-init-function nil))) 
        (error (condition) 
          <Handle the error some way here>))))) 

Please note that the information in the (printed) IDE User Guide (online in doc/cg/ide-user-guide/) is slightly incorrect on this point. It says in section 4.14.4 that the project init function must return a window. That is not true, as the text of this FAQ item says. It can return anything but if it returns anything other than a window (that is still open), the program will exit immediately.


© Copyright 1999, Franz Inc., Berkeley, CA.  All rights reserved.
$Revision: 1.1.2.13 $