CG/JS documentation

About Common Graphics on HTML5/JavaScript (CG/JS)

Common Graphics (CG) has been ported to HTML5/JavaScript. This means that CG applications can now in web browsers, rather than as desktop Microsoft Windows or GTK programs. A short name for this platform is CG/JS.

CG/JS has several advantages. On Mac and Linux, apps look nicer in a web browser than on GTK (version 2), where there is no antialiasing or gradient shading and the fonts do not scale well to some sizes. GTK does not need to be installed on Mac and Linux, and the XQuartz X11 server is not needed on the Mac. Apps have a consistent look and feel in web browsers across Mac, Linux, and Windows. The user can zoom any entire CG application in and out with the browser’s usual keystrokes for that, without any code changes in the CG app. And the html-widget works on all platforms for embedding web pages or HTML files in a widget of your application.

A special CG/JS advantage is the client/server model, where the application can be running as a server on one machine, and then a user can connect to it remotely from another machine, using only their favorite web browser and without installing any software on their own machine. This access can even be across the Internet if the server machine allows that. A single running app could also allow multiple simultaneous clients if it is written to handle that.

This document covers all of the new CG/JS functionality, as well as other small post-10.1 CG and IDE enhancements that are not specific to CG/JS mode. Within each section, the things that are expected to be more of interest are roughly more toward the top of that section.

Quick Start

To try out an existing CG project in web browser mode, first run the IDE as usual and open the project. Then go to the The CG/JS Tab of the Project Manager in the IDE and turn on Run as Web Browser Server at the upper left. Then Run | Run Project will run the app in a web browser, and generating the standalone app will run it in a web browser by default.

The IDE itself can also run in a web browser. You could immediately run it in a browser by starting it from a command line that includes -b yes. Otherwise you could start it as usual, then turn on Tools | Options | IDE 1 (tab) | Run the IDE in a Web Browser, and then exit the IDE and restart it.

Overview of Features

The officially supported web browsers (which we test) are Chrome, Firefox, Safari, Edge, Opera, Brave, and Vivaldi, though any other Chromium-based browsers are expected to work. The only still-active browser that is known to be unsupportable is Internet Explorer, because its JavaScript support is out of date and apparently will not be updated.

In Safari only, save-pixmap is not working.

Any CG App Can Run in Desktop Mode or Web Browser Mode

When you develop a CG application as a project in the IDE and then generate a standalone executable for it, that application can be run in either desktop mode or web browser mode. (Desktop mode is often called Windows mode or GTK mode, and web browser mode is often called CG/JS mode.) You can specify the default mode when you generate the app, and then the user can use a command line argument to run in the other mode if needed.

Allegro’s IDE (where you can develop CG applications) is itself a CG application, and it can run in either desktop mode or web browser mode as well. The IDE’s Options dialog has a check box on the IDE 1 tab called “Run the IDE in a Web Browser”, which determines the mode in which the IDE will start up by default on successive runs.

The Project Manager Has a New Tab for CG/JS Options

There is a new tab in the Project Manager dialog in the IDE whose label is “CG/JS”. Here you can specify the default startup mode along with options for web browser mode. See The CG/JS Tab of the Project Manager in the IDE.

The Web Browser’s Full-Screen Mode Can Be Used As Usual

The web browser’s own title-bar and toolbars reduce the amount of screen space that’s left for your CG application. But the browser’s usual command for toggling full-screen mode can be used to give your application as much screen space as in desktop mode.

The usual keystrokes for this are Control+Command+F on a Mac or F11 elsewhere, so those keystrokes by default are in a project’s Browser Keychords option in the Project Manager to let the browser handle them.

The User Can Zoom the Entire CG App

The user can use the web browser’s standard commands for zooming (or scaling) the entire application, without any changes to application code. These commands usually use the keystrokes Control+Plus, Control+Minus, and Control+Zero (or the Command key instead of Control on a Mac) to zoom in or out or return to the standard zoom level. These keystrokes are included by default in the project’s Browser Keychords option to let the browser handle them itself, though they could be removed to disable zooming or if the application needs to use these keystrokes for its own commands.

When the user zooms the application, the generic function screen-resolution-changed will be called, and you can add methods that respond to the change. To the CG application it looks like the screen size has changed, and it can be handled in the same way. Typically an application might resize some windows to fit ideally into the new screen size.

If zooming is not working in Firefox, it may be that you need to turn off the “Zoom Text Only” option on the General tab of Firefox options.

Changes You Might Need to Make in Your CG Application

The goal is for existing CG apps to run in web browsers with no changes, though there are some known exceptions.

The Variables for Special Icons Are Now Functions

The five icon variables error-icon, warning-icon, question-icon, information-icon, and application-icon are now functions (which take no arguments). So each occurrence of error-icon in your code would need to become (error-icon). These values are often passed to pop-up-message-dialog, for example. Unfortunately there’s no way to conditionalize this in CG/JS loaded it into a 10.1 lisp, other than with something like (if (fboundp 'error-icon)(error-icon) error-icon).

Additional CG Processes Need to Be Started in a Certain Way

If your application creates additional processes that call CG functions, then they need to be started either by using in-cg-process or by adding a binding for *system* to the usual *default-cg-bindings* to pass as the :initial-bindings argument to process-run-function. It’s OK to do this for both desktop mode and web browser mode. For example:

(mp:process-run-function
  (list :name "Second CG Process"
        :initial-bindings
        (cons (cons '*system* *system*) *default-cg-bindings*))
  'second-process-startup-function)

You May Need to Call cg-process-wait Rather than process-wait

If you have calls to process-wait where the wait test depends on events getting handled, then you will need to convert those to calls to cg-process-wait. The reason is that events can be handled during a process-wait only on the Windows platform.

You May Need to Filter Events If Dragging Operations Are Sluggish

Due to the communication between lisp and the web browser, certain dragging operations may be more sluggish in web browser mode. To alleviate that, you can call the setf functions of mouse-move-filter, scroll-filter, and wheel-filter to tell the browser to send an event of that type only whenever a specified number of milliseconds have passed. The browser will still send the final event of a series even when less time passed before that one. For example:

(setf (mouse-move-filter my-window) 100) ;; ten times a second

You May Want to Use an Alternate Drawing Mode for Efficiency

A JavaScript window that can be drawn on arbitrarily always has backing store that holds what has been drawn on the entire scrolling canvas. CG still draws only what has been scrolled into view by default, but if it is more efficient for a particular kind of window to draw everything initially rather than if and when it gets scrolled into view, then you can define a draws-the-entire-scrolling-page method that returns true to tell CG that your redisplay-window method does that. (This is more in line with the JavaScript approach.)

Always Do Exit Cleanup in User-Close Methods

It’s already highly recommended that you do any cleanup that’s needed at exit time in a user-close method for a top-level window, because if you do it only in a File | Exit command (for example), then the user could bypass that by closing the top-level window directly. Any interactive gesture to close a top-level window will call user-close on that window, and so a user-close method can ensure that your cleanup code is run.

Bypassing a File | Exit command may be even more common in CG/JS, because if the user closes your app with its official exit command, then they would be left with a blank browser tab that they still need to close. So most users will likely close the browser tab directly, meaning that your cleanup code really needs to be in a user-close method (which will still get called).

Avoid Prompting the User in a User-Close Method

There’s no way to prevent the browser from handling a Control-F4 gesture to close the app’s browser tab, in order to show a “Do you really want to exit?” dialog first. So your user-close method should avoid showing a modal dialog or other prompt if web-browser-has-disconnected returns true (meaning that the app was running in a web browser but the connection is now gone). If the user has closed the browser tab, then there is no longer an environment in which to show a modal dialog that CG would wait on, and the app could hang while waiting for the browser to reply.

Dragging Loops Might Need to Call process-pending-events

Code that has run only on Windows and which has busy loops (such as for dragging operations) that exit based on calls to functions like key-is-down-p and mouse-button-state may now need to call process-pending-events inside the loop in order for the loop to exit when running in CG/JS mode (or GTK mode).

Setting the Default Web Browser for CG/JS in Windows 11

A possibly confusing thing in Windows 11 is that a CG/JS app will invoke the web browser that’s associated with the HTTP link type (or URL scheme) rather than the one that’s associated with the .html file type. So it’s not sufficient to use the Settings app in Windows 11 and for example enter “.html” in the box at the top of the “Default apps” page and then select the desired web browser. The best way appears to be to go to the child page for the desired browser and press the button at the upper right to make it be the default browser, and then verify that it has set that browser not only for the .html and .htm file types that are listed below, but also for the HTTP and HTTPS link types that are listed farther below.

The CG/JS Tab of the Project Manager in the IDE

This tab contains one option at the upper left for whether to run in web browser mode or desktop mode, followed by other options that apply when running in a web browser. These values are used with Run | Run Project and when running the generated application.

When running the generated app, the default values specified here can be overridden for a particular run with command line arguments. See Command Line Options when Running a CG App in a Web Browser.

Run as Web Browser Server

Whether the generated application will run by default in a web browser or as a desktop application. When the IDE is running in desktop mode, Run | Run Project will also use this mode, though when the IDE is running in a web browser, a project cannot be run in desktop mode.

When running the project in a web browser, the app will appear in a new tab of the same browser. The command Run | Stop will do nothing, and you can instead simply close the browser tab for that run when you are done with it, and that will kill the process for that run.

The word “Server” in this option name alludes to the fact that the application always runs as a server when running in web browser mode, even when the Start Local Client option is used to start a client automatically.

The default value of this project option can be overridden with the --run-as-web-browser-server command line argument when starting up the generated application, unless the Disallow Running In Non-Default Mode option was enabled when generating the standalone application.

This option can be read or set programmatically with the run-as-web-browser-server accessor of the cgjs-options of the project.

The rest of the options apply only when running in a web browser.

Start Local Client

Whether running the generated application will also tell the default web browser on the same machine to connect to it as a client and display the application. Turning this option on causes the app to run like an ordinary standalone application that a user runs on their own machine.

Turning this option off causes the app to run as a server that one or more users can connect to at any time from their web browsers on the same machine or other machines, without installing any software on their own machine. This remote access could be within a local area network, or across the Internet if the server machine is open to the Internet.

If the app is run as a server (with this option off) and its Browser Server Port is 12345, then a user could connect to it by telling their web browser to visit localhost:12345 if the server and browser are on the same machine, or else with [machine-name]:12345, where [machine-name] is the name of the server machine.

The default value of this project option can be overridden with the --start-local-client command line argument when starting up the generated application.

This option can be read or set programmatically with the start-local-client accessor of the cgjs-options of the project.

Exit Server on Client Exit

Whether the application server will exit automatically whenever the last client has disconnected. This is typically used whenever Start Local Client is used, in the general mode to behave like an ordinary standalone application. When this option is on, the user can simply close the browser tab and be assured that everything exits.

Turning this option off allows the same user or different users to connect to the application server at different times as the server continues to run (or even at the same time if Max Clients is greater than one). This mode requires using some facility of the operating system (or else the Show CG/JS Server Window option) to kill the server eventually.

Note that when a user exits by closing the browser tab, that will bypass your app’s official exit command and any cleanup that it does. So an app that runs in web browsers needs to follow the usual paradigm where you write a user-close method for your main window that does the same cleanup. Closing the browser tab directly will call user-close on each top-level window, allowing you to do cleanup in your user-close methods.

The default value of this project option can be overridden with the --exit-server-on-client-exit command line argument when starting up the generated application.

This option can be read or set programmatically with the exit-server-on-client-exit accessor of the cgjs-options of the project.

Confirm Exit

Whether the web browser will prompt the user for exit confirmation when they try to close the web browser tab for the application or reload the web page. Turning this on may avoid losing unsaved changes in the application.

The default value of this project option can be overridden with the --confirm-exit command line argument when starting up the generated application. While the app is running, it could call the setf of confirm-web-browser-exit to update the state that will be used in the rest of that session of the app.

This option can be read or set programmatically with the confirm-exit accessor of the cgjs-options of the project.

Disallow Running In Non-Default Mode

Whether an application that is built to run in web browser mode by default will be prevented from being run in desktop mode with a command line argument, or vice versa.

When this is enabled, running the standalone application will simply ignore any --run-as-web-browser-server or -b command line argument that was specified. So this option can be used if you intend to support only browser mode or only desktop mode, in case an end user were familiar with that command line option.

This option can be read or set programmatically with the disallow-running-in-non-default-mode accessor of the cgjs-options of the project.

Limit Connections to Same Machine

Whether a web browser will be able to connect to the CG application server only if it’s running on the same machine as the server. This would prevent, for example, anyone from connecting from elsewhere on the Internet if the server machine is not behind a firewall.

This option can be read or set programmatically with the limit-connections-to-same-machine accessor of the cgjs-options of the project.

Show CG/JS Server Window

Whether an additional browser tab will appear for the CG application server, where you can exit the server by closing that tab or the window that’s inside it. This tab will appear in a browser on the server machine. This is probably useful only for an administrator who is running the app as a server on one machine, allowing one or more users to connect to it from browsers on other machines. This avoids needing to use an operating system facility to kill the server when not using Exit Server on Client Exit.

The default value of this project option can be overridden with the --show-cgjs-server-window command line argument when starting up the generated application.

This option can be read or set programmatically with the show-cgjs-server-window accessor of the cgjs-options of the project.

Show CG/JS Logging Window

Whether the server window will have a child window that displays logging information, such as whenever a user connects or disconnects. This is probably useful only to an administrator when users are working on other machines, to know when there are active users. Show CG/JS Server Window must also be on when using this option.

The default value of this project option can be overridden with the --cgjs-logging command line argument when starting up the generated application.

This option can be read or set programmatically with the cgjs-logging accessor of the cgjs-options of the project.

Title for Browser Tab

The short string to display as the name of the application in the small browser tab above the application.

This option can be read or set programmatically with the title-for-browser-tab accessor of the cgjs-options of the project.

There is no project option for the “Loading …” message that appears in the browser until the first window gets shown (to let the user know that something is happening), because that message appears before the browser has contacted lisp and before lisp has responded to the browser with information about the application. But you could edit that string constant in the copy of cgjs.html that gets distributed with an application.

Browser Keychords

The set of keystrokes that CG’s JavaScript code will pass through to the browser for its built-in behavior. This allows the user to use common web browser commands directly, because there appears to be no way to invoke them programmatically. This also means that the app cannot use these keystrokes for its own commands.

Here is the default value, where the second row is for when the app is run on a Mac and the first row is for when it is run on other platforms:

("F11" "Control+0" "Control+-" "Control+="
 "Meta+0" "Meta+-" "Meta+=" "Control+Meta+F")

The key names need to be JavaScript names, where “Meta” is the JavaScript name for the Mac’s Command key. On a Mac, pressing F while holding down the Control and Command keys is the way to toggle full-screen mode, while F11 does that on other platforms. The other keystrokes allow the user to zoom the entire application. This is covered further under the variable *cgjs-client-options*.

This option can be read or set programmatically with the browser-keychords accessor of the cgjs-options of the project.

Browser Server Port

The port at which a web browser can connect to the CG application server. If this option is nil or zero, then the app will let the operating system select a port that is known to be free, and the port will be printed to the terminal where the app is being run when possible (probably not on Windows). If the Start Local Client option is yes, then the browser will connect to this port automatically, and so you don’t need to know the port number for entering it into a web browser.

When specifying a particular port, it should be between 1 and 65535, inclusive. If an attempt to use that port fails, then the OS will be asked to select a free port, as when specifying zero.

The value can alternately specify a range of ports, as in 12800-12804. Then each port in the range will be tried until one is found that does not fail. This may be needed when a firewall allows only ports that are in that range. If the --launcher command line option is used, then the ports of all launched instances of the application will also be restricted to this range (and so the range should be at least as big as the max-clients option).

Specifying a particular port may be somewhat risky because it would likely fail or lead to confusion if other software is already using that port. And specifying the same hard-coded port for multiple runs of the app could lead to confusion if a previous run did not actually exit for some reason, because then the browser will likely attempt to connect to the old server process instead of the new one. But you may need to use this option anyway when you are not starting a local client automatically and you’re starting the app from a terminal that will not print an OS-selected port, because otherwise you would not know what port to tell a web browser to visit. Or use the --port-file command line argument to place the OS-selected port into a file where you can retrieve it.

The port that gets used will become the value of *browser-server-port*.

The default value of this project option can be overridden with the --browser-server-port command line argument when starting up the generated application.

This option can be read or set programmatically with the browser-server-port accessor of the cgjs-options of the project.

Max Clients

The maximum number of clients that can connect to the running CG server at the same time. This applies when running the app as a server and letting people connect to it from their web browsers. If someone tries to connect when this many clients are already connected, then their browser will show a message saying that the maximum number of clients are already connected.

If the number is greater than 1, then the application needs to be written in a way that works with multiple simultaneous clients. Each client will run in its own process to be independent of other clients, but it would still be a problem if the app stores data in global places like global variables when that data needs to be distinct for each client. The macro defvar-cg could be used to declare variables that automatically get a separate binding in each process; it otherwise works like defvar.

The default value of this project option can be overridden with the --max-clients command line argument when starting up the generated application.

This option can be read or set programmatically with the max-clients accessor of the cgjs-options of the project.

Style Options

Options for things such as the color and thickness of window borders, the font used in all menus, and the delay before showing a child menu. Stylistic properties like these are usually system-wide parameters that affect all apps in a windowing system. There is no equivalent in the web browser world, so CG/JS defines default values in the variable *cgjs-client-options*, which holds a large association list of them. See that variable for the complete alist.

This project property can contain any subset of the *cgjs-client-options* entries, except with different values to override the defaults. The custom values will be used in the standalone app that’s generated from the project, and when running the project in the IDE.

The Project Manager widget for this is rather small, so if you want make significant changes you may want to do it programmatically, by using the style-options accessor of the cgjs-options of the project.

Here’s an example that specifies bolder cyan/blue custom colors for window borders, with one color for the currently selected window and a second color for other windows.

(setf (ide:style-options (cg:cgjs-options (ide:current-project)))
  `((:selected-window-border-color
     ,(make-rgb :green #xaa :blue #xff))
    (:unselected-window-border-color
     ,(make-rgb :red #x77 :green #xcc :blue #xff))))

An application could allow its users to specify some of these style options as well, and then write them to a file named foo-style-options.txt in the user’s home directory (or the personal Documents directory on Windows), where foo is the name of the application’s executable file. The content of the file should be an alist that contains some subset of *cgjs-client-options*, and then they will be used on subsequent runs of the app. The form (utilities-directory :hidden t) returns the directory where the file should be placed. See utilities-directory.

You can also customize these style options for the IDE itself (rather than for your application). See web-browser-style-options.

JavaScript Files to Import

A list of JavaScript code files that CG will automatically import at run time by calling import-javascript-file. This allows the application to call the file’s exported JavaScript functions by calling the CG function call-js. Each element in the list should be a pathname or path namestring that is relative to the project folder, where you would typically place the JavaScript files. So you could place two files named one.js and two.js (for example) into the project folder and then set the value of this option to this:

("one.js" "two.js")

The files will then automatically be imported when you run the project in the IDE. And when you generate the standalone application for the project, the JavaScript files will be copied to the distribution and imported when you run the generated app.

(You can alternately run a single form in the IDE, but that will not re-import the files each time. So to test changes that you’re making to the JavaScript files during that IDE session you would need to run the project instead.)

This option can be read or set programmatically with the javascript-files-to-import accessor of the cgjs-options of the project.

Include Modules for Starting Local Client

If the generated application will always be run as a server, and will never be run with the Start Local Client option, then you could turn this option off so that the build will not necessarily include some CG modules that are needed for that. That may reduce the size of your distributed app a little, but it’s likely not significant.

Include Modules for CG/JS Logging

If the generated app will never be run with the Server Window or Logging Window options, then you could turn this option off to reduce the size of your distributed app somewhat by not necessarily including the CG modules that those features need.

Command Line Options when Running a CG App in a Web Browser

Here are the command line arguments that can be used to specify whether to run the app in desktop or web browser mode, and when running in web browser mode whether to use various CG/JS options. These options apply to a CG app that was generated from a project in the IDE, and to the IDE itself. Using these options requires starting the app with a command line in a terminal or script rather then interactively (such as from Finder on the Mac or the Start menu on Windows). The default values can be specified on The CG/JS Tab of the Project Manager in the IDE, to avoid using a command line when running with the usual options. Command line arguments always override the defaults that are built into the app.

In each case, you can type either the short version of the command name that starts with a single dash, or the long version that starts with two dashes, followed by one of the valid values that are indicated on the right.

An application could allow its users to specify some of these startup options for subsequent runs of the app, and then write them to a file named foo-startup-options.txt in the user’s home directory (or the personal Documents directory on Windows), where foo is the name of the application’s executable file. The content of the file should be the actual string for the options as they would appear in a command line. So the file might contain only -b yes if the user requested that the application start up in a web browser next time. The form (utilities-directory :hidden t) returns the directory where the file should be placed. See utilities-directory.


--run-as-web-browser-server

Short version: -b

Value: yes or no

yes means to run the app (or the IDE) in a web browser and no means to run it as a desktop Windows or GTK program. See Run as Web Browser Server in The CG/JS Tab of the Project Manager in the IDE.

This command line argument will be ignored if the Disallow Running In Non-Default Mode option in the IDE’s Project Manager dialog was enabled when generating the standalone application.

The rest of these options are used only when running in a web browser.


--start-local-client

Short version -l

Value: yes or no

yes means to automatically tell the default web browser on the same machine to connect to the CG server and display the application. no means to wait for someone to explicitly connect to the server from a web browser that could be on another machine. See Start Local Client in The CG/JS Tab of the Project Manager in the IDE.

If you run the application in a terminal and specify this command line argument as no, then it might appear that things are hung because the terminal where you ran the app does not get a fresh prompt (unless you add an ampersand to the end of the command line to run the app in the background). But actually the app is running and waiting for someone to connect from a web browser. When the user closes the browser tab, the app will exit and the terminal will get a fresh prompt.


--client-timeout

Short version -k

Value: a non-negative integer

If this is a positive integer and --start-local-client is yes, but no client connects within that many seconds, then the server will exit. If this option is not specified or its value is zero, then a default timeout of 12 seconds will still be used if the --start-local-client option is yes. This ensures that the server application will not be left running invisibly if no web browser successfully connects to the server when that was requested. There is no corresponding project option for this one.


--exit-server-on-client-exit

Short version -x

Value: yes or no

Whether the lisp will exit automatically if a user closes the browser tab where they are using the application and there are no remaining clients connected to the server. See Exit Server on Client Exit in The CG/JS Tab of the Project Manager in the IDE.


--launcher

Short version: none

Value: yes, no, or only

Whether this lisp application will listen for launch HTTP requests that a web page can send to request a separate instance of the application for use by that web page only, where typically it will embed the application in an HTML iframe or a new browser tab. This built-in feature allows web pages to remotely launch a CG/JS application without using a separate application server. This is useful when the application is not written in such a way that a single instance of the application can handle multiple clients simultaneously, and so a separate running executable must be used for each client.

If this command line option is passed as yes or only, then the Max Clients option in The CG/JS Tab of the Project Manager in the IDE is reinterpreted to mean the maximum number of instances of the application executable that can be running at the same time. And a single instance will never serve multiple clients.

If the value is only, then this launcher instance will act only as a launcher and never serve a client itself, instead always launching a new instance (up to the Max Clients limit). When the value is yes, the launcher instance itself will be used for a requesting client whenever it is not currently serving a client. Using the launcher instance will start up the application more quickly because it doesn’t need to run a new lisp executable, though then the launcher instance perhaps might not respond as quickly to another request for an instance if the application is busy at the moment.

In the normal case, the response that is returned to the web page for its launch request will be the port at which the web page can use its private instance of the application, on the same machine as the launcher instance. Typically a launcher instance is running at a known port, and a web page will first send a launch request to that port and receive the port for its own instance, and then tell an iframe or new browser tab to vist the machine at that port to display the application. The launch HTTP command looks simply like host:launcherPort/launch, or like host:launcherPort/launch?password=secret if the --remote-password option was also used when running the launcher instance.

If an application implements its own command line arguments, then a web page could pass them as the value of a command-line-args argument to the launch HTTP command.

If the launcher refuses to serve the launch request (typically because the Max Clients are currently running, then the response to the launch request will be zero. If a --remote-password was also specified when running the launcher, but it was not passed correctly in the launch request, then the response is -1.

When an instance was launched by another one, then it will accept an exit HTTP request to exit that lisp. (A launcher instance will also accept an exit request, but it will do nothing because that could exit a different client if the application was exited without closing the browser tab and then another client is now running in the launcher instance.) A web page might want to send this request before it sends another launch request to restart the CG application, so that it works even when the maximum number of instances are currently running. If a --remote-password was specified when running the launcher instance, then it must be passed with this request to any launcher or launched instance.

See An Example Web Page for Embedding a CG/JS Application for a complete JavaScript example of embedding a CG app in a web page.

There is no corresponding project option for this command line option.


--remote-password

Short version: none

Value: a string

When specified, this is a password that a web page must include in any launch request to remotely launch its own instance of the application, and in any exit request. See the --launcher option above.

An alternative is to set the CGJS_REMOTE_PASSWORD environment variable. That may be more secure because then the password will not be shown when listing processes with the Unix ps command (though that would show it only on the server machine).

There is no corresponding project option for this command line option.


--file-to-publish

Short version: none

Value: a file path

This option can be used to specify an HTML file to publish as a web page that can visited on the machine where the CG/JS application is running. This could be used along with the --launcher option (for example) to publish a demo JS/HTML file that a user could visit from their web browser, where that demo file could have a button that sends a launch command to this application to embed its own instance of the application in an iframe.

The path of the published URL will be the name of the file, without its file type. So if the file is specified as /home/me/cool-demo.html (for example), then the path of the URL to visit will be cool-demo, and a user could visit the page by directing their web browser to host:port/cool-demo (substituting the needed values for host and port).

One warning: If you exit the CG app that has published a file, modify the file-to-publish, and start the CG app again (specifying the same file-to-publish), then visiting the URL of the published page again in your browser will likely display the previous version of the file-to-publish, even if you do this in a new browser tab. The previous behavior of the file will still be in place as well. This has confused the developer to no end. 8-) You will then need to use your browser’s refresh command to force it to load the modified file so that you can test your most recent changes.

There is no corresponding project option for this command line option.


--limit-connections-to-same-machine

Short version: none

Value: yes or no

Whether a web browser will be able to connect to the CG application server only if it’s running on the same machine as the server, typically for security reasons. See Limit Connections to Same Machine in The CG/JS Tab of the Project Manager in the IDE.


--show-cgjs-server-window

Short version -s

Value: yes or no

Whether an additional browser tab will appear on the server machine for the CG application server, where you can exit the server by closing that tab or the window that’s inside it. This may be useful in server mode by an administrator. See Show CG/JS Server Window in The CG/JS Tab of the Project Manager in the IDE.


--cgjs-logging

Short version -g

Value: yes, no, or a file path

If yes, then the server window (if present) will have a child window that displays logging information, such as when a user connects or disconnects or uses a menu command. The --show-cgjs-server-window option must also be yes for this to take place.

If a file path, then logging information will be incrementally appended to that file. If the --launcher option was used, then any instances that are launched by this one will also append information to the same file.

See Show CG/JS Logging Window in The CG/JS Tab of the Project Manager in the IDE


--browser-server-port

Short version -o

Value: a non-negative integer

The port at which a web browser can connect to the CG application server. See Browser Server Port in The CG/JS Tab of the Project Manager in the IDE.

Not specifying this option or passing it as zero will let the operating system select a port that it knows to be free.

The following example (when substituting the actual executable name for [appname]) would run the app in web browser mode, but not start a local client automatically, and use port 9009. A user could then run the app in a web browser on the same machine by telling the browser to visit localhost:9009, or on a different machine by telling the browser to visit [machine-name]:9009.

[appname] -b yes -l no -o 9009 

--max-clients

Short version -m

Value: a positive integer

The maximum number of clients that can connect to the running CG server at any one time. See Max Clients in The CG/JS Tab of the Project Manager in the IDE.


--confirm-exit

Short version: none

Value: yes or no

Establishes whether the web browser will prompt the user for exit confirmation when they try to close the browser tab or reload the page. See Confirm Exit in The CG/JS Tab of the Project Manager in the IDE.


--port-file

Short version -i

Value: a file path

A file path to which CG will write the free port that the OS selected for use as the Browser Server Port. This file could be read to find the port to which a web browser needs to connect to display a CG app that’s running in server mode. There is no corresponding project option for this one.

If specified, this should be either a full pathname such as /home/me/my-app-port.txt or c:\foo\free-port-for-my-app.txt, or else just a file name and type such as my-app-port.txt to indicate the path ~/.my-app.d/browser-port.txt on Linux and Mac (if .my-app.d was specified as the utility-file-directory of the project) or a path like c:\Users\me\Documents\browser-port.txt on Windows. The browser server port number that was used will then be printed into the file with no other text.

This could be useful on Windows when an unknown free port was selected by the OS but it could not be written to the terminal where the app was run. An administrator who ran the app could then find the port in the file and pass it on to users, for example. It might also be useful in a custom script to find the port that was used, where the script should first delete the file if it exists, then wait until the file exists again, and finally read the port from the file.


--secure

Short version -a

Value: do not specify an argument

When specified, the app will run in a secure mode to protect the machine that the app is running on from possible nefarious or accidental actions that could remove files, for example. This is probably useful mainly when running the app in client/server mode, where an administrator runs the app on a server machine and then a user connects to it from a browser on a different client machine. There is no corresponding project option for this one.

Specifically, the file selection dialog is never shown because it would allow the user to browse the whole file system on the server machine, and write files most anywhere. All files will be read from and written to a single folder that is returned by (utilities-directory), which defaults to *utilities-directory* if non-nil, or else to what home-directory returns. When the app calls ask-user-for-existing-pathname, a simple dialog will list the names of the files of the appropriate type that are in this directory, and when ask-user-for-new-pathname is called, the app will just ask for a file name to be typed in. See utilities-directory.

The value of the CG variable *secure-mode* will be true if and only if this command line argument was used.


--utilities-directory

Short version -y

Value: a file path

Sets the variable *utilities-directory* to the specified file path (a string), which causes utilities-directory to return it.


New CG Functions and Variables

js-mode

[new CG function]

(defun js-mode (&optional process)

Returns true if and only if the specified process (which defaults to the current process) is running in CG/JS mode rather than Windows or GTK desktop mode. This could be called to determine the mode in which a user is now running your application (as long as you do not call it on a non-CG process).

web-browser-name

[new CG function]

(defun web-browser-name ()

Returns a keyword like :chromium, :safari, or :firefox, indicating which brand of web browser the user is using. :chromium is used for all of the Chromium-based browsers, including Chrome, Edge, Opera, Brave, and Vivaldi. Returns nil when running in Windows or GTK desktop mode.

web-browser-os

[new CG function]

(defun web-browser-os ()

Returns one of the keywords :mac, :windows, or :linux, for the OS of the machine where the user is running their web browser. This can be a different machine than where the CG app is running as a server. Returns nil when running in Windows or GTK desktop mode.

displaying-on-mac

[new CG function]

(defun displaying-on-mac ()

Returns true if the user is on a Mac or nil otherwise.

In CG/JS, the returned value reflects the platform where the web browser is running, rather than where the CG lisp application is running, which may be different when the CG app is running in server mode.

This function may be convenient when defining the event-synonym of a menu-item, when the keychords are different on the Mac (such as by employing the Command key, which is called meta-key in CG). Otherwise each such case would need to use the functions js-mode and web-browser-os along with #+macosx and #-macosx conditionalizations to determine the proper key to use in every case. This function does that for you. For example:

   (make-instance 'menu-item
     :value 'reload
     :title "~Reload"
     :event-synonym (cond ((displaying-on-mac) '(meta-key #\R))
                          (t '(control-key alt-key #\R))))

control-or-command-key

[new CG function]

(defun control-or-command-key (&optional return-symbol-value)

Returns meta-key when the user is on a Mac or control-key otherwise. In CG, meta-key is used for the Command key on a Mac. If return-symbol-value is true then the symbol itself is returned, and otherwise the value of the symbol is returned.

In CG/JS, the returned value reflects the platform where the web browser is running, rather than where the CG lisp application is running, which may be different when the CG app is running in server mode.

This function may be convenient when defining the event-synonym of a menu-item, when the Command key is used on a Mac and the Control key on Windows and Linux, and that is the only difference. For example:

   (make-instance 'menu-item
     :value 'paste-command
     :title "~Paste"
     :event-synonym (list (control-or-command-key) #\V))

web-browser-has-disconnected

[new CG function]

(defun web-browser-has-disconnected
  (&optional (process mp:*current-process*))

This function is meaningful only in CG/JS mode, and will always return nil in Windows mode and GTK mode. In CG/JS mode it will return nil while the web browser is still connected, but return true after the web browser has disconnected. The function windowp will return nil when this function returns true.

If an application user-close method does something that waits on a reply from the user, typically showing a “Do you really want to exit?” dialog, and it may run in CG/JS mode, then it should first call this function and avoid doing the prompt if it returns true. Otherwise it could hang waiting on a reply from the disconnected browser and prevent lisp from unwinding and exiting (which it normally does when the browser has disconnected).

draws-the-entire-scrolling-page

[new CG modifiable generic function]

(defmethod draws-the-entire-scrolling-page ((window basic-pane))

This generic function is used only by CG/JS, though it is available everywhere for cross-platform code.

The default method returns nil, but an application can supply a method for a window class that returns true to trigger a variation on if and when portions of the window’s scrolling canvas get drawn.

Returning true declares that the window’s redisplay-window method always draws the entire scrolling canvas (see page-width and page-height) as needed to keep the whole canvas up-to-date, rather than drawing only the currently-visible window interior as usual.

When true is returned, scrolling a window will not bother to draw the part of the canvas that gets scrolled into view, because it is known to be already drawn. That saves time when scrolling, though it requires drawing the whole canvas initially even though much of it may never get scrolled into view. So this is a trade-off that may be better for some kinds of windows.

When true is returned and invalidate is called on the window with no box argument, it will invalidate the entire scrolling canvas of the window rather than just the currently visible portion as it does otherwise. That will be reflected in the box argument that is passed to the window’s redisplay-window method. Therefore, redrawing can be optimized in all cases by making a redisplay-window method always draw only things that intersect its box argument.

This is applicable only in CG/JS mode because every window has a backing-store memory bitmap in JavaScript for the whole scrolling canvas (except in small-canvas-mode). In Windows mode or GTK mode, this is true only of a bitmap-pane or a bitmap-stream.

See also small-canvas-mode for a different approach that saves memory when a window has a large scrolling canvas. (It is not useful to use small-canvas-mode and draws-the-entire-scrolling-page together.)

small-canvas-mode

[new CG function and property]

(defun small-canvas-mode (window-or-widget)

This property has an effect only in CG/JS mode. It should always be specified by passing a :small-canvas-mode initarg to make-window or to make-instance for a widget, though the function could be called later to see whether a window is in this mode.

When nil (the default), drawing in the window works in the standard JavaScript way where the window has a JavaScript canvas that’s as large as the whole scrolling page (see page-width and page-height). The JS canvas remembers everything that has been drawn that can be scrolled into view. And if you have added a draws-the-entire-scrolling-page method that returns true, then scrolling the window does not require anything to be redrawn, which is the complete Javascript convention.

When true, the JavaScript canvas will be only as large as the interior of the window. This still avoids redrawing the window when it gets uncovered, but scrolling will always redraw the content that gets scrolled into window. (For efficiency, you should not supply a draws-the-entire-scrolling-page method that returns true for a window that’s in small canvas mode.)

Small canvas mode is needed if the page-width or page-height of the window is greater than 32767, because the browser will not make a JavaScript canvas that large because of the memory that it requires to save the value of every pixel. CG will automatically switch a window into small canvas mode whenever a page-width or page-height greater than 32,000 is requested, so an application doesn’t need to handle that itself. But you may want to use small canvas mode even at smaller page sizes to reduce memory consumption and the time that it takes to swap in fairly large JavaScript canvases.

Small canvas mode is the way that CG always works in Windows mode and GTK mode, and is the natural mode for those systems. It may be that the only big advantage with NOT using small canvas mode is when using it along with draws-the-entire-scrolling-page for extra fast scrolling, because then nothing is redrawn while scrolling. Yet small canvas mode is off by default in order to use the usual JavaScript convention.

In the default mode where neither small canvas mode nor draws-the-entire-scrolling-page is used, scrolling content into view for the first time will leave the area that’s newly scrolled into view momentarily blank until your application draws the newly-visible content for the first time. So you may want to use one of these two optional modes to avoid a flashing effect.

For use with a widget (dialog-item) rather than a custom application window, small canvas mode may be useful only with a few kinds of widgets that might have large scrolling pages. And it is not useful for widgets of type os-widget (including item-list and list-view) because they are assembled from HTML elements and do not use a JavaScript canvas at all. So its usefulness would be limited to widgets of type lisp-widget. And while the grid-widget and the chart-widget do scrolling, they use special techniques where this mode would not come into play. That leaves the outline and scrolling-static-text widgets as the only ones where small canvas mode is likely to be useful. The main outline in the IDE’s trace dialog uses small canvas mode, and therefore can collect many thousands of traced calls.

A :no-canvas-mode initarg can alternately be passed as true to make-window when drawing is never done on that window. That will save memory by not creating a JavaScript canvas for the window at all. The window will still display its background-color.

mouse-move-filter

[new CG setfable function]

(defun mouse-move-filter (window)

Returns or sets the value of a filter for mouse-moved events in the specified window. The default value is nil, to not filter events at all. If activities that use the events (such as dragging operations) get behind, then this can be set to a number of milliseconds. The JavaScript code in the web browser will then send an event only after this many milliseconds have passed since it last sent an event. An exception is that the last event in a series will always be sent, so that the CG application is informed of the final state of that series.

mouse-move-filter, scroll-filter, and wheel-filter are used only when running in a web browser.

This example would cause my-window to never receive a mouse-moved event within a tenth of a second after another one, except when it is the last event in a series.

(setf (mouse-move-filter my-window) 100)

scroll-filter

[new CG setfable function]

(defun scroll-filter (window)

This works like mouse-move-filter, except for scroll-to events. This typically applies when the user is dragging a scroll-bar of a window.

wheel-filter

[new CG setfable function]

(defun wheel-filter (window)

This works like mouse-move-filter, except for mouse-wheel events. These events come in when turning an actual mouse wheel, and sometimes are emulated by certain drags on a trackpad.

confirm-web-browser-exit

(defun confirm-web-browser-exit ()

Returns whether the web browser will prompt the user for exit confirmation when they attempt to close the web browser tab or reload the page. The setf of this function can be called at run time to establish whether the web browser will do that for the rest of the current session. The default value is nil. Setting it to true can avoid losing unsaved changes in the CG app. This function has an effect only in CG/JS mode.

The default value for the generated application of a project can be set with the Confirm Exit widget in The CG/JS Tab of the Project Manager in the IDE. The default value for the IDE can be set with the query-web-browser-exit IDE option. Those defaults can be overridden at startup timeq with the --confirm-exit command line argument.

(setf (confirm-web-browser-exit) t)   ;; avoid losing changes

web-browser-clipboard-action

[new CG modifiable generic function]

(defmethod web-browser-clipboard-action
  ((top-level-window t)(window t) string paste?)

This function is called after a copy or paste has been done when running in CG/JS mode, to confirm that the operation was done. An application can add methods to this function that specialize on the app’s own window subclasses, to handle these notifications in some custom way.

top-level-window is the top-level-window of the window where the operation was done. This is a parameter to allow specializing methods on that window class when that is more convenient than specializing on the actual window where the operation was done.

window is the window where the operation was done.

string is the text that was copied or pasted.

paste? is true when the operation was a paste and nil when it was a copy.

Copy & paste is tricky in JavaScript due to security restrictions, and so this function may be useful for confirming that it really happened. Though there is only one known case where it may not actually happen: Some browsers will not do a requested copy if a certain amount of time has passed between when the user typed Control+C (or Command+C on a Mac) and when the event got passed to CG and then CG tells the browser to programmatically do a copy. In that case CG will show a modal dialog that asks the user to type Control+C again while that dialog is present, to let the browser handle the keystroke itself and do the copy. If the user does not follow that instruction and type Control+C again at that time, then no copy will be done, and this generic function will not be called.

defvar-cg

[newly-exported CG macro]

This macro works just like defvar, except that every CG process that’s created will automatically get its own binding for the symbol.

This may be useful when enhancing an application so that it works with multiple simultaneous web browser clients. (That is allowed when the Max Clients option of the project is greater than one.) That would not work if client-specific information is stored in global variables, so converting defvar forms to defvar-cg is a simple way to handle that particular problem.

default-process-for-events

[new CG setfable function]

(defun default-process-for-events ()

This function is used only in CG/JS mode. It returns nil by default, but the setf of this function can be called to specify a particular process that should receive events that are not associated with a window. The large majority of events are associated with a window, and those events get dispatched to the creation-process of the window. Other events will get passed to the initial CG process unless the setf of this function is called to specify some other process. Timer events in particular are not associated with any window, and there may be others.

(setf (default-process-for-events) my-process-for-windowless-events)

send-dummy-message-to-window

[newly-exported CG function]

(defun send-dummy-message-to-window (window)

This function should not be needed by applications, but it might come in handy anyway in certain situations for waking up the creation-process of the specified window more quickly than it would happen otherwise.

*browser-server-port*

[new CG variable]

The port at which CG/JS is communicating with one or more web browsers. If a particular port was specified for the Browser Server Port property of a project or with a --browser-server-port command line argument, then the value will be that port number. Otherwise it will be the free port that the operating system selected.

*secure-mode*

[new CG variable]

The value of this global variable is true if the app was run in web browser mode with the --secure command line argument, which avoids using the general file selection dialog for security reasons.

*default-cgjs-external-format*

[new CG variable]

The external format that will be used by default in CG streams when running in CG/JS mode. The default value is (crlf-base-ef :utf-8s). The “s” at the end of the name stands for “strict”, and makes recovery easier after an error such as printing a substring that starts or ends with an unpaired surrogate lisp character that’s part of a single extended Unicode character (one that’s beyond the 16-bit range). In particular, in CG/JS this avoids a problem otherwise where printing such a bad substring to a CG stream would leave the stream in a state where correct extended Unicode characters fail to print correctly.

CG/JS also sets the global variable utf-8s-transcoding-error-action to :count, for use in debugging. If errors such as printing unpaired surrogates do happen, then the value of that variable will change to the number of bad characters that have been encountered so far. You could monitor the value of that variable to see if your application code is using invalid substrings.

You could set this variable to (crlf-base-ef :utf-8) if you would rather that errors get signaled, to help track down the source of the problem, though that might lead to repeating errors when trying to view a backtrace.

Alternately, you could set this variable to simply :utf-8 or :utf8-s if you would like files on the Windows platform to be read and written with CR/LF newlines rather than LF newlines. Whenever CG is about to use this external format with internal websocket code where CR/LF newlines are not appropriate, it always first calls crlf-base-ef on the value of this variable to obtain the actual external format to use, so that is not a problem.

utilities-directory

[new CG function]

(defun utilities-directory (&key hidden child-directory-name)

Returns the pathname of a directory where it is suitable to save utility files for the current user, such as option files. An application could call this function to determine the directory to save such files in.

The returned directory will be the value of *utilities-directory* if it has arbitrarily been set to a file path instead of its default value of nil. Otherwise, the directory will basically be the user’s home directory, defined on Windows as the user’s Documents directory. Appended to this directory will be the child-directory-name argument if it has been passed as a string (it should not contain slashes), or else the utility-file-directory of a project if the project is now running as a standalone application; though on Linux this child directory is appended only if the hidden argument is true. On Windows, and on Mac when hidden is nil, any leading “.” or “.d” will first be stripped off of the child directory name, because that is a Linux convention. And on Mac when hidden is nil, the child directory will be used as a child directory of the Documents child directory of the user’s home directory.

This function is provided mostly for application use, though one time that CG uses it internally is to look for a file with a name like appname-startup-options.txt (where appname is the name of the executable file) when a generated application is starting up. The application itself needs to write that file in this directory (passing hidden as true) if it has asked the user for startup options. That file can contain a single line of CG/JS command-line arguments that will be used for subsequent runs, such as -b yes to run in web browser mode. So the application might have a “Run In Web Browser” checkable menu-item, and if the user toggles it on then the app could write -b yes into this file, and the app will run in a web browser next time.

utility-path

[new CG function]

(defun utility-path (base-file-name &key hidden child-directory-name)

Returns a file pathname where it is suitable to save the specified utility file like foo.txt for the current user. This merges base-file-name with the directory that’s returned by calling utilities-directory with the same hidden and child-directory-name arguments, and returns that pathname.

One exception on Linux and Mac is that if hidden is true and this is not a standalone application whose project has a utility-file-directory, then a period is prepended to base-file-name, as is customary for hidden files.

home-directory

[new CG function]

(defun home-directory ()

Returns the user’s home directory on Linux or their personal Documents directory on Windows, or nil if somehow there isn’t one. This may be useful for finding a directory in which to save utility files for the current user. In case it is informative, here is the source code:

(defun home-directory ()
  #-mswindows
  (pathname-as-directory (or (system:getenv "HOME")
                             (translate-logical-pathname "sys:")))
  #+mswindows
  (or (win:special-windows-directory :my-documents)
      (win:special-windows-directory :personal)))

*utilities-directory*

[new CG variable]

The initial value of this variable is nil, but if it is set to a pathname (or path namestring) then it will be returned by utilities-directory, overriding the usual path that it returns, which is either the user’s home directory or a child directory of it.

When an application stores files in the utilities-directory, this can be used to establish a custom utilities directory.

This variable can be set by passing the --utilities-directory command-line argument.

utility-file-directory

[new CG generic function]

(defmethod utility-file-directory (project)

If this project property is non-nil, then it should be a string that names a subdirectory, without any slashes in the string. In that case, the function utilities-directory will append that subdirectory to the directory that it returns when the project is running as a standalone application.

The value can be set interactively in the Utility File Directory widget on the Options tab of the Tools | Options dialog.

media-player

[new CG class]

(defclass media-player (os-widget)

The parent class of audio-player and video-player. The API for those two classes is nearly identical and is therefore shared. These widgets are currently implemented only for CG/JS mode, though they could also be implemented for Windows.

Each player has interactive controls for the usual actions such as playing and pausing, moving the current time, and adjusting the volume. These actions and others can also be performed programmatically by calling the functions media-player-command and media-player-property.

audio-player

[new CG class]

(defclass audio-player (media-player)

A widget that plays audio files, typically mp3 files. It is currently implemented only for CG/JS mode, though it could also be implemented for Windows mode as a widget on top of the mci-wave-audio API. It shares the media-player API with the video-player widget. The value property of the widget is the file path namestring of the audio file that the widget is currently playing or ready to play.

video-player

[new CG class]

(defclass video-player (media-player)

A widget that plays video files, typically mp4 files. It is currently implemented only for CG/JS mode, though it could also be implemented for Windows mode as a widget on top of the mci-animation API. It shares the media-player API with the audio-player widget. The value property of the widget is the file path namestring of the video file that the widget is currently playing or ready to play.

media-player-command

(defmethod media-player-command ((widget media-player) command-name)

Starts or stops the playback of an audio-player or video-player widget. The command-name should be one of the keyword symbols :play or :pause.

Pausing playback will leave the current playback time where it paused, and playing will begin from the current playback time. The current playback time can be set arbitrarily with media-player-property.

media-player-property

(defmethod media-player-property ((widget media-player) property-name)

Returns the current value of one of the properties of an audio-player or video-player widget. The property-name should be one of the keyword symbols in (:display-controls :volume :current-time :duration :paused :ended :muted :looping :autoplay :playback-rate).

The setf of this function can be called to modify most of the properties, namely any in (:display-controls :volume :current-time :muted :looping :autoplay :playback-rate). The others are not modifiable.

Only the :display-controls property can be specified at widget creation time, by passing a :display-controls initarg as true or nil, where the default value is true. The other properties can be set only with this function.

Here are the meanings of the various properties:

:display-controls - Whether the player displays interactive gadgets for playing and pausing and setting the current playback time and so on. Otherwise the audio-player is not seen at all. Some browsers may always display the controls for the video-player.

:volume - The current playback loudness, from 0.0 (silent) to 1.0 (loudest).

:current-time - The number of seconds from the beginning at which the file is currently playing or will begin playing.

:duration - The total length of the media in seconds.

:paused - True when the media is not currently playing.

:ended - True when the media has finished playing to the end.

:muted - True when the audio is silent.

:looping - True when the media will begin playing from the beginning when it reaches the end.

:autoplay - True when the media will begin playing automatically as soon as it is ready. This may be considered impolite.

:playback-rate - The speed at which the media is currently playing or will play. For example, 1.0 is normal speed, 0.5 is half speed, and 2.0 is double speed.

display-controls

(defmethod display-controls ((widget media-player))

Returns whether the :display-controls initarg was passed as true to make-instance when creating an audio-player or video-player widget. Calling the setf of this function will have no effect; the value must be passed as an initarg at creation time.

*opaque-rubber-banding*

[new CG variable]

When t, the standard dragging operations like get-line and get-shape-box will use a newer and nicer drawing style than the traditional XORing that is used when this variable is nil, as it is initially.

The opaque style has no flashing like the old XOR style, and draws with the usual colors. It may be slower, though, and may not work at all with the “shape” functions depending on how the custom draw-fn and erase-fn that are passed to those functions are written.

This variable should not be set (with setq or setf) because it doesn’t work in all cases and the functions that use it are sometimes called internally. Instead, it can be bound to t around calls to the dragging functions where it is found to work well.

The opaque style is generally always used in CG/JS mode because the XOR style is not implementable in JavaScript. One exception, though, is that if one of the “shape” functions is called with a draw-fn that always redraws everything, along with an erase-fn that does nothing, then the opaque style will probably not work (and will not be needed). For those cases, you can bind this variable to :never so that the opaque style is not used even in CG/JS mode (though that kind of custom draw-fn typically means that it’s not really doing XORing).

show-tooltip-explicitly

[new CG function]

(defun show-tooltip-explicity
    (window string-list
       tooltip-horizontal-center tooltip-top
       &key (justification :center)(font (tooltip-font))
       allow-over-status-bars)

Shows a tooltip at an arbitrary time and place. This is a lower-level function than the tooltip facility for widgets and hotspots, where you simply assign a tooltip string to a widget, and then CG takes care of showing it in a standard place below the widget after a delay when the mouse cursor is moved over the widget.

This function simply displays a tooltip window at some arbitrary place when you call the function. You need to decide if and when to call this function, and where to place the tooltip window, and then you need to call hide-tooltip when you want the tooltip to go away. CG sizes the tooltip window for you so that the text fits within it.

window is the window over which the tooltip will be displayed.

string-list is a list of strings, where each string will appear as one line of text in the tooltip window. You could call split-string-to-width to convert a string into the multiple lines of text.

tooltip-horizontal-center is the x coordinate of the horizontal middle of the tooltip window in the stream coordinates of window. Typically you would specify this as the horizontal middle of a thing that’s drawn in the window, which the tooltip is describing, to align the tooltip window horizontally with whatever it is describing

tooltip-top is the y coordinate of the top of the tooltip window in the stream coordinates of window. Typically you would specify this as a coordinate that’s a little below the bottom of a thing that’s drawn in the window, which the tooltip is describing, to place the tooltip just below whatever it is describing.

justification is how the lines of text are horizontally aligned with each other in the tooltip window, and can be either :left, :center, or :right. This has no effect when there is only one line of text.

font is the font in which the text will be printed in the tooltip window.

allow-over-status-bars determines whether to avoid placing the tooltip over a status bar, where it may obscure the text in the status bar. When true, the tooltip will be placed at the specified coordinates as usual. When nil, the tooltip will be scooted away from that position to avoid obscuring the status bar.

display-pixmap-in-browser-tab

[new CG function]

(defun display-pixmap-in-browser-tab
    (pixmap &key surrounding-color title)

Displays the specified pixmap in a new web browser tab. This function should only be called if js-mode indicates that CG is running in CG/JS mode. surrounding-color, if not nil, should be an RGB object (see make-rgb) for the background color that will surround the pixmap in the new tab. title, if not nil, should be a string to display in the small rectangle above the tab interior (which the user clicks to select that tab).

The new browser tab is not a CG tab, so the user could right-click the image and select the browser’s standard “Save Image to File” command to save the pixmap to a file on the client machine. (The CG function save-pixmap saves a file on the server machine.)

list-widget-focus-index

[new CG setfable generic function]

(defmethod list-widget-focus-index ((widget item-list-or-list-view))

Returns the index of the item in an item-list or list-view widget that currently has the keyboard focus. This is not necessarily the same item as the selected value, especially for a multi-item-list or when the multiple-selections property of a list-view is true.

The setf of this function can be called to move the focus to a different item without also changing the selected value. This is done internally when you type the first one or more characters of a list item in a multiple-selection widget, when you likely do not want to select the item. You can then press the spacebar to select the focused item if desired.

scroll-text-cursor-into-view

[previously undocumented CG function]

(defmethod scroll-text-cursor-into-view
    ((text-edit-pane text-edit-pane)
     &key (lines-of-padding 0)(above t)(below t))

Scrolls a text-edit-pane vertically so that the text cursor is scrolled into view. It scrolls farther than needed by lines-of-padding, so that there are at least that many lines of text visible both above and below the text cursor for context (when possible). If the text cursor is currently scrolled off the top of the window, then it will be scrolled into view only if above is true. Similarly, if the text cursor is currently scrolled off the bottom of the window, then it will be scrolled into view only if below is true.

text-edit-pane-send-enter-to-server

[new CG modifiable generic function]

(defmethod text-edit-pane-send-enter-to-server
    ((pane text-edit-pane))

This function is used only in CG/JS mode. An application could add methods that return true for text-edit-pane subclasses that implement custom handling when the user types the Enter key, where the browser widget should not directly add a newline character.

CG/JS generally lets the web browser directly handle keystrokes in a text-edit-pane to immediately add typed characters to the widget and move the text cursor around and so on. Otherwise text editing would likely be sluggish if every keystroke were sent to the CG server and then the CG server told the browser widget to programmatically insert a character or move the text cursor.

But the browser still sends most keystrokes to the CG server after that, for any side effects that an application implements. So virtual-key-down methods will still be called for a text-edit-pane.

But sometimes an application may need to handle the Enter key entirely itself, without letting the browser add a newline character at all. When that is the case, adding a method to this generic function that returns true for that text-edit-pane subclass will prevent the browser from inserting the newline character before it sends it to the CG server. If what the application does with the keystroke includes inserting a newline character at some point, then the CG code needs to do that itself, such as by calling insert-character.

CG will prevent the browser widget from inserting any typed characters until the CG application is done handling the Enter key, and then the browser will insert the typed characters that were queued up. This prevents characters from being inserted in the wrong order.

font-face-fixed-width-p

[newly-exported CG function]

(defun font-face-fixed-width-p (face)

Returns whether the specified font face names a fixed-width font, which is one where all characters have the same width.

face should be a string that names a font face. It should be one of the strings that are returned by the function font-faces, and which can be passed as the face argument to make-font-ex.

This function may be more convenient than font-fixed-width-p.

send-undo-keystrokes-to-server

[new CG modifiable generic function]

(defmethod send-undo-keystrokes-to-server
    ((pane widget-window))

Text-editing widgets in JavaScript implement their own multi-level undo and redo, which CG could not reasonably implement. So CG/JS lets the web browser handle Control+Z and Control+Y for those directly, rather than sending those keystrokes to the CG server as usual.

If an application instead needs to do some sort of custom undo and redo for a text-edit-pane or editable-text-pane subclass, then it should add a method to this generic function that returns true for that subclass. The browser will then send those keystrokes to the CG server for custom handling.

effective-value-axis-range

[new CG generic function]

(defmethod effective-value-axis-range ((axis value-axis))

Returns a list of the bottom and top (or left and right) of the current effective range of the specified value-axis of a chart-widget. This may be needed when not explicitly setting the range-bottom and range-top properties, to find the range that the widget automatically chooses as round values that surround all of the current data values.

exclude-or-include-chart-object

[new CG modifiable generic function]

(defmethod exclude-or-include-chart-object ((widget chart-or-plot)
                                            chart-object exclude?)

If the click-to-toggle-inclusion property of a chart-widget or plot-widget is true, then this generic function is called whenever the user clicks on an object in the legend to toggle whether that object is currently included in the chart or plot. An application could add a method for a chart or plot subclass to implement side effects when this happens, such as to re-sort the values.

widget is the chart-widget or plot-widget.

chart-object is the chart-object whose legend entry was clicked.

exclude is whether the click toggled the object off rather than on.

start-text-editing-on-grid-widget-focus

[new CG setfable generic function and grid-widget property]

(defmethod start-text-editing-on-grid-widget-focus
  ((grid-widget grid-widget))

If the edit-start-trigger of the focus-cell of the grid-widget is :get-focus, then this is whether text editing will begin automatically when the grid-widget itself gets the keyboard focus, rather than only when the focus shifts within the grid-widget to the cell.

no-focus-box

[new CG modifiable generic function and editable-text property]

(defmethod no-focus-box ((widget editable-text))

Returns whether an editable-text or multi-line-editable-text widget will let a web browser’s built-in text-editing control draw its focus rectangle when it has the keyboard focus. The value can be set by calling the setf of this function, or at creation time by passing the :no-focus-box initarg.

That built-in focus box can appear heavy-handed in some cases. CG’s grid-widget turns it off for the editable-text that’s used for editing text in a grid cell, because it’s redundant with the cell focus box that’s drawn by CG (and the cell’s borders).

highlight-item-under-mouse

[new CG setfable generic function and outline widget property]

(defmethod highlight-item-under-mouse ((outline outline))

When true, moving the mouse cursor over the widget will highlight each outline-item when the mouse cursor is over it. This might help with reliably clicking the intended item. The default value is nil.

The highlighted outline-item under the mouse cursor will be drawn in the colors that are returned by outline-item-highlighted-foreground-color and outline-item-highlighted-background-color.

outline-item-highlighted-foreground-color

[new CG modifiable generic function]

(defmethod outline-item-highlighted-foreground-color
  ((outline outline)(outline-item outline-item))

The color in which to draw the text in the outline-item that is currently under the mouse cursor when highlight-item-under-mouse is true. Applications could define methods that specify custom colors for outline subclasses. The default method returns the color that is the variable of the variable black.

outline-item-highlighted-background-color

[new CG modifiable generic function]

(defmethod outline-item-highlighted-background-color
  ((outline outline)(outline-item outline-item))

The color in which to draw the background of the outline-item that is currently under the mouse cursor when highlight-item-under-mouse is true. Applications could define methods that specifiy custom colors for outline subclasses, or even to use different colors for different outline-item subclasses in the same widget. The default method returns the color that is returned by (make-rgb :red 192 :green 248 :blue 255).

drop-outline

[new CG generic function]

(defmethod drop-outline ((outline dropping-outline)
                         &optional no-set-focus-p)

This can be called to programmatically show the dropping pane of a dropping-outline, without the user clicking on the widget as usual to show it. It has no effect if the dropping pane is already shown. The keyboard focus will move to the dropping pane unless no-set-focus-p is true.

This function is called internally when the user clicks on the widget to show the dropping pane, so an :after method could be added for side effects, such as showing a help message.

undrop-outline

[new CG generic function]

(defmethod undrop-outline ((outline dropping-outline)
                           &optional no-set-focus-p)

It probably wouldn’t be nice for an application to call this function to programmatically hide the dropping pane of a dropping-outline that the user is looking at. But it might be useful to add an :after method for side effects, such as moving the keyboard focus to some other widget.

named-gradient

[new CG generic function]

(defmethod named-gradient ((stream graphical-stream))

Returns the current named gradient for the specified stream, if one was set by calling set-named-gradient, and otherwise nil.

set-named-gradient

[new CG generic function]

(defmethod set-named-gradient
  ((stream graphical-stream) gradient-name)

This is a convenience function on top of the *color-gradient-filling* facility, for using a particular hardcoded set of gradient settings rather than binding the four individual special variables. The only gradient-name that has been implemented is :rounded-top-and-bottom, which causes area-filling functions like fill-box to draw a convex rounded look that is lighter toward the top. This is used for nodes in Franz’s Gruff graph database browser. Call clear-named-gradient to remove the effect, or use with-named-gradient to both set and clear the effect.

clear-named-gradient

[new CG generic function]

(defmethod clear-named-gradient ((stream graphical-stream))

Clears a gradient effect that was set with set-named-gradient. Use with-named-gradient to do both. This function simply sets the four special variables that include *color-gradient-filling* to nil.

with-named-gradient

[new CG generic macro]

(defmacro with-named-gradient ((stream gradient-name) &body body)

Causes filling functions like fill-box in body to use a special hardcoded gradient effect during the execution of body. See set-named-gradient.

fill-bezier-curve

[new CG function]

(defmethod fill-bezier-curve ((stream graphical-stream) vertices)

This is implemented only in CG/JS mode, and will do nothing in Windows mode and GTK mode. It is like draw-bezier-curve except that it fills the interior of the curve.

split-string-to-width

[previously undocumented CG function]

(defun split-string-to-width
  (string width stream &key delimiters
    (split-only-at-newlines-when-present t))

Separates a string into lines of text that will fit into a specified pixel width. Returns a list of substrings of the specified string, splitting the string where any of the specified delimiter characters occur beyond the specified pixel width. The individual lines of text could then be drawn onto a stream within that pixel width.

Each element in the returned list is a list of (1) the substring for one line, (2) the pixel width of that substring, (3) the starting index of the substring within the original string, and (4) the ending index.

When #\space is a delimiter it will be removed, but other delimiter characters will be retained. This is to emulate the way the underlying windowing system wraps text in the CG function draw-string-in-box, but using this function instead allows the app to know where the wrapping is done.

string is the lisp string to split.

width is the width in pixels into which the string will be wrapped.

stream is the stream onto which the string will be drawn, in case the underlying windowing system uses a different pixel width for text in different kinds of graphical streams, even when using the same font.

delimiters is a list of characters at which the string can be split. When unspecified or nil, it will default to *text-wrapping-delimiters* if *wrap-text-in-cg* is true, and otherwise it will default to a list of only the space character.

split-only-at-newlines-when-present, when true, will cause the string to be split only at newline characters when there are in fact one or more newline characters in the string.

*text-wrapping-delimiters*

[new CG variable]

A list of the characters at which split-string-to-width will divide a string into multiple text lines by default when *wrap-text-in-cg* is true. The initial value is this:

(#\space #\- #\_)

*wrap-text-in-cg*

[new CG variable]

When this variable is true, split-string-to-width will use *text-wrapping-delimiters* by default rather than only the space character as the characters at which it will split text into multiple lines. The initial value is nil.

Apps could bind or set this variable to true so that any calls to split-string-to-width will use that alternate behavior. The only place that CG internally calls split-string-to-width internally is in the undocumented nodes-and-links facility that Franz’s Gruff graph database browser uses.

write-backtrace-to-file

[new CG function]

(defun write-backtrace-to-file (condition)

This is an alternate value for the default-error-handler-for-delivery option of a project, for debugging a generated application that crashes or fails to start up. When an otherwise unhandled error is signaled, it will print the current function stack backtrace into a file named cg-backtrace.txt in the system temporary directory, where that path is returned by this expression:

(merge-pathnames "cg-backtrace.txt" (sys:temporary-directory))

The application will continue to run after this, though it may be in an inconsistent state. The only sign that an error has occurred may be that the generated app stops responding; in that case, you could check the content of that file (if it exists) when using this function.

close-menu-and-submenus

[previously undocumented CG function]

(defun close-menu-and-submenus (menu)

Closes the specified menu and all of its descendent cascading submenus, by recursively calling cl:close on each one. CG does not do this automatically because it allows adding a menu as a child of multiple parent menus.

Closing menus is likely only needed in rare cases to avoid depleting limited resources of the underlying Windowing system, in particular the Microsoft Windows limit of 32,767 total menu-items in an application.

See also close-window-and-menus.

close-window-and-menus

[previously undocumented CG function]

(defun close-window-and-menus (window)

Closes the specified window, and also calls close-menu-and-submenus on the menu-bar of the window, if it has one.

request-a-dummy-reply-from-web-browser

[new CG function]

(defun request-a-dummy-reply-from-web-browser (&optional minimum-milliseconds)

This function may be useful in certain cases where the web browser is delaying the refreshing of the screen while it is busy handling many requests from lisp. This function simply sends a message to the browser that requests a dummy reply. For reasons that are not clear, sending the reply will trigger the browser to refresh the screen at that time. The lisp IDE uses this during a Find In Files search.

Calling this function frequently can slow down the operation that’s being done. To alleviate that, minimum-milliseconds can be passed as a positive integer. In that case, the request to the browser will be sent only if it has been at least that many milliseconds since the previous time that this request was sent. Passing 500, for example, would ensure that the display is refreshed at least every half second, without slowing things down much.

*use-alternate-websocket-nudge*

[new CG variable]

This one is too obscure to explain, but it may make some custom dragging loops smoother in CG/JS mode. The usual value is nil, but if you have implemented some sort of custom dragging loop that’s not as smooth as you would like when running in CG/JS mode, then you could test binding this variable to true around that loop, and keep that change if it happens to make the dragging smoother. Calling idle-until-the-next-event inside the loop might also help in some loops when this variable is true. See also process-pending-events about possible hangs in dragging loops.

idle-until-the-next-event

[new CG function]

(defun idle-until-the-next-event ()

When *use-alternate-websocket-nudge* is true, calling this function in CG/JS mode will idle without consuming CPU until the next event has been received from the web browser, allowing the event to be dispatched to a CG process when that otherwise might not happen during a busy loop.

It may make some custom dragging loops smoother in CG/JS mode if you bind *use-alternate-websocket-nudge* to true around the loop and call this function inside the loop.

toolbar-icon-height

[new CG function]

(defun toolbar-icon-height ()

Returns the height of toolbars that are created by add-toolbar. In Windows mode and GTK mode this returns *toolbar-icon-height*, while in CG/JS mode it returns *toolbar-icon-height-js*. Those variables could be modified to use a different toolbar height, but this function should be called to return the height in any mode.

*toolbar-icon-height-js*

[new CG variable]

The height of toolbars that are created by add-toolbar when running in CG/JS mode. The initial value is 22. This variable could be modified at startup time to make all toolbars be a different height, though the function toolbar-icon-height should be called to return the height in any mode.

[new CG variable]

This is used in CG/JS mode in the same way that *modal-dialogs-disable-owner* is used in Windows mode and GTK mode. The initial value is t. You could set it to nil if graying out other windows while showing a modal dialog is slow in CG/JS mode, but that has not been seen to be a problem as it has been in GTK mode.

start-cgjs-server

[new CG function]

(defun start-cgjs-server ()

Starts the CG/JS server. Typically there is no need to call this function, because it is called automatically whenever running a standalone application in CG/JS mode, as long as the application was generated from a project in the IDE. It is also called when running a project in CG/JS mode while the IDE is running in desktop mode, and when running the IDE itself in CG/JS mode. But it could be needed when loading CG into a base lisp or when generating a CG app from scratch without using the project system. It needs to be called once in any lisp before doing any other CG/JS activity other than calling initialize-cg.

Here is a typical sequence of forms to evaluate to load CG into a base lisp and then start up CG/JS and run a CG function. The :app-function would normally be something much more significant than this trivial example.

(require :cg)
:pa cg-user
(setf (cgjs-server-option :run-as-web-browser-server) t)
(defun my-app-function ()
  (make-window :foo :class 'text-edit-window))
(setf (cgjs-server-option :app-function) 'my-app-function)
(initialize-cg)
(start-cgjs-server)
(start-cgjs-client)

If you modify my-app-function, you can test the updated version by just closing the previous browser tab and calling (start-cgjs-client) again.

start-cgjs-client

[new CG function]

(defun start-cgjs-client ()

Starts a CG/JS client to show the running CG app in the default web browser on the machine where the CG application is running. It does this by telling the default web browser on the same machine to visit a URL like localhost:12345 (if the CG app’s Browser Server Port option is 12345), just as if a user interactively entered that URL into the browser’s URL widget.

Typically there is no need to call this function, because it is called automatically whenever running a standalone application in CG/JS mode, as long as the application was generated from a project in the IDE and the project’s Start Local Client option is enabled. It is also called when running a project in CG/JS mode while the IDE is running in desktop mode, and when running the IDE itself in CG/JS mode. But it could be needed when loading CG into a base lisp or when generating a CG app from scratch without using the project system.

tell-web-browser-app-is-exiting

(defun tell-web-browser-app-is-exiting ()

Tells the web browser that the app is exiting. Calling this just before exiting lisp can be useful for a couple of things: (1) If the user exits the app without closing the browser tab, then when they do close the empty browser tab later the browser will not prompt for confirmation when the Confirm Exit option was used. (2) CG will stop intercepting any browser keystrokes that the user might want to use in the empty tab, such as Control+L to move to the browser’s URL widget.

Typically there is no need to call this function, because it is called automatically whenever a standalone CG/JS application from a project exits, and whenever the IDE in CG/JS mode exits. But it could be needed when loading CG into a base lisp or when generating a CG app from scratch without using the project system.

cgjs-client-option

[New CG setfable function]

(defun cgjs-client-option (option-name)

Returns one of the values in *cgjs-client-options*. Calling the setf of this function directly has no effect in the usual scenarios, and there are other facilities for modifying these options. See *cgjs-client-options*.

cgjs-server-option

[New CG setfable function]

(defun cgjs-server-option (option-name)

Returns one of the values in *cgjs-server-options*.

Calling the setf of this function directly is not supported. It would likely have no effect because the values have already been used once lisp has started up, and it might cause unforseen confusion.

*cgjs-server-options*

[New CG variable]

An association list that holds options that affect how the CG/JS server works, and which are used only in lisp code on the server side. These option values will be in common for all of the multiple clients that are sharing the same running CG/JS server.

This alist could be examined for informational purposes, or by calling cgjs-server-option on individual option names. But this alist should not be modified directly. To set the values for a project, use The CG/JS Tab of the Project Manager in the IDE.

import-javascript-file

[new CG function]

(defun import-javascript-file (path)

Dynamically loads a JavaScript code file at run time, which allows integrating pre-existing JavaScript code with a CG/JS application that’s written in lisp. Then the CG function call-js can be called in lisp to call JavaScript functions that are in that file.

path is a pathname or path namestring of the file to import. The file will be imported by first publishing the file with AllegroServe, and then importing it in the JavaScript code with the import function. The call to this function will return only after the import has completed, and so calls to imported functions should work immediately after that.

For a JS function to be callable after importing the file, it must be exported. A single export statement could be added at the end of the JS file (for example) for exporting everything that should be callable from lisp:

export { doSomething, doSomethingElse };

When developing a project in the IDE, a convenient alternative is to set the JavaScript Files to Import option on the CG/JS tab of the Project Manager dialog. Then running the project will call this function automatically to load the JS files. And generating the standalone application will copy the JS files to the distribution, and this function will be called to load those files when running the application.

call-js

[new CG function]

(defun call-js (alist module &key reply-needed read-reply-from-string
                      even-when-exiting)

Calls a function in custom JavaScript code that’s running in a web browser for a CG/JS application, after import-javascript-file was called at run time to load the JS file. The possibilities for this function are not described completely here, but this may be enough for making basic calls into JavaScript.

The alist argument will be described last, because its entry is long.

The module argument is the pathname-name of the JavaScript file that was loaded by calling import-javascript-file. This is used for cleanly separating the namespaces of multiple JavaScript files that might get imported and the namespace of the internal JS code of CG. It also fits well with the way that the JavaScript import function works.

If reply-needed is true, then call-js will not return until the JS function that was called returns, and the internal JS code of CG then sends a reply to lisp. call-js will then receive as a string whatever the JS function returned, and will return that string. If you would like to return a set of values, then your JS code could call encodeLispArgs (a CG function) on a JS array of values, and then lisp will receive a string that the lisp reader would read as a lisp list.

If read-reply-from-string is true, then call-js will call read-from-string inside an error handler on the string that was received from the web browser, and then return either the value that was read or nil if an errored was signaled. If your JavaScript code called encodeLispArgs on a JS vector to derive the string to return, then this should result in call-js returning a lisp list.

If even-when-exiting is true, then call-js will make the call even if lisp has begun the process of exiting. Otherwise it will not make the call because that might interfere with lisp exit, and so it will immediately return nil. In no case will it attempt the call if it is known that the web browser has disconnected, such as by the user closing the browser tab.

The main alist argument is an association list that contains entries for the name of the function that’s being called and the arguments to pass to it. For example, this code snippet emulates how the CG function move-window tells the JS code to do the move:

  (call-js
   `((fun "moveWindow")
     (args ,(list (handle window)
                  (position-x position)(position-y position))))
   "one") ;; the pathname-name of the JS file that was imported

That calls a JavaScript function that looks like this:

function moveWindow (handle, x, y) {
  var frame = windows[handle];
  var fstyle = frame.style;
  fstyle.left = x + "px";
  fstyle.top = y + "px";
}

You generally should not call built-in CG functions like that one, because CG (in lisp) would not be aware of that and may get confused. But you can call your own JavaScript code in a similar way, and you could even import a file named “one.js” that exports its own function named moveWindow and call it as in that example.

CG internally uses that form of the alist argument for almost all of its calls, where the alist has a “fun” entry with the name of the JavaScript function to call, followed by an “args” entry with the arguments to pass. The alist keywords like “fun” and “args” are symbols, but the package of the symbols that you use does not matter because only their string names are passed to JS.

An alternate format for calling call-js uses individual named arguments in the association list instead of a single “args” argument. This is similar to passing keyword arguments in lisp. If the moveWindow example were written that way, then the lisp code might look like this, with argument names as alist keys:

  (call-js `((fun "moveWindow")
             (handle ,(handle window))
             (x ,(position-x position))
             (y ,(position-y position)))
           "one") ;; the pathname-name of the JS file

Then the JavaScript function would be called with a single JS object whose property names are those alist keys, and JS code can extract the individual values from that object by name:

function moveWindow (args) {
  var frame = windows[args.handle];
  var fstyle = frame.style;
  fstyle.left = args.x + "px";
  fstyle.top = args.y + "px";
}

Those examples show how CG passes a window to JavaScript by sending its “handle”, which in CG/JS is an integer. This is done because lisp objects would make no sense in JavaScript, of course. Then the JS code finds its HTML element for the window by using that integer as an index in a global “windows” vector that maps window handles to the HTML elements that serve as windows in the web browser. A JS vector works because the window handles are sequential positive integers beginning with 1, while zero is reserved for the screen. So windows[1] in the JavaScript code will return the first window that was created, while windows[0] will return the element that acts as the logical screen for the CG/JS app. (Or you could use the variable cgScreen, which holds the CG screen element.)

If you want your JS code to use windows that were created by CG, then you may need to do something like adding a layer of functions that accept a CG handle, then find the CG window element for the handle, and pass that to one of your existing functions. Something like this:

function doSomethingFromCG (cgWindowHandle) {
  doSomething(windows[cgWindowHandle]);
  }
function doSomething (htmlElement) {
  ... }

If you instead want to pass references to HTML elements that your JS code has created, then you could for example pass the HTML “id” attribute of the element instead of a CG window handle, and then find the HTML element itself in your JS code by calling document.getElementById as usual. Or use document.querySelector from scratch in your JS code.

One paradigm that may be useful is using an existing JS function that draws content to draw that content in a CG window. This would need to deal with the design where any regular CG window has an outer parent HTML element for the CG border and title bar, which has a child element for the interior of the window, and that child has a child that’s a JS canvas for drawing on. CG stores the interior of a parent in an “interior” attribute, and the canvas of the interior element in a “canvas” attribute. So a custom JS function for drawing something in a CG window could begin like this to find the context for passing to JS drawing functions:

function drawMyDiagram (cgWindowHandle) {
  let canvas = windows[cgWindowHandle].interior.canvas;
  let ctx = canvas.getContext("2d");

Some types of lisp values get converted automatically as needed for passing to JavaScript. The symbol nil will be passed as the JavaScript value false, while t will be passed as true. Other symbols will be passed as their symbol name strings, with no package qualifier. Numbers will be passed as numbers. A CG rgb color object (as created with make-rgb) will be passed as a JS RGBA color string like "#64ffc8ff". Any non-empty lisp list will be passed as a JavaScript array, and elements of a list will be converted in a similar way (but only a single level down).

Another paradigm might involve calling existing JS code that creates its own HTML elements, but placing the top-level element into a CG window or onto the logical CG screen. That would allow mixing trees of custom JS elements with trees of CG windows, though it’s less obvious what typical use cases may be.

As a final more complete example, here is code from a trivial sample project that demonstrates the two paradigms mentioned above. First, here is the JavaScript code, which exports a drawCheckerBoard function to draw a checker board in random colors inside a CG window, and a newElement function that creates a custom HTML element in a CG window or on the CG screen:

function drawCheckerBoard (handle) {
  let canvas = windows[handle].interior.canvas;
  let ctx = canvas.getContext("2d");
  let count = 8, size = 40, color, useFirstColor = true;
  let colorOne = randomColor(), colorTwo = randomColor();
  for (let x = 0; x < count; x++) {
    for (let y = 0; y < count; y++) {
      if (y > 0) useFirstColor = !useFirstColor;
      color = useFirstColor ? colorOne : colorTwo;
      ctx.fillStyle = color;
      ctx.fillRect(x * size, y * size, size, size);
    } } }
function newElement (handle, left, top, width, height) {
  let parent = windows[handle];
  parent = parent.interior || parent;
  let element = document.createElement("div");
  let previousElement = parent.currentElement;
  if (parent.currentElement) parent.removeChild(previousElement);
  parent.currentElement = element;
  let style = element.style;
  let borderColor = randomColor(), borderWidth = 4, padding = 8;
  let interiorDecrement = 2 * (borderWidth + padding);
  style.display = "block"; style.position = "absolute";
  style.padding = padding + "px"; style.borderRadius = "16px";
  style.background = "#bbddeecc";
  style.left = left + "px"; style.top = top + "px";
  style.width = width - interiorDecrement + "px";
  style.height = height - interiorDecrement + "px";
  style.border = borderWidth + "px solid " + borderColor;
  element.innerText = "This border color is " + borderColor + ".";
  element.fontSize = 14;
  style.fontSize = element.fontSize + "px";
  element.onmousedown = function () {
    this.fontSize += 1; // increase the font size when clicked
    this.style.fontSize = this.fontSize + "px";
  }
  parent.appendChild(element);
}
function randomColor () {
  return "rgb(" + random(256) + "," +
    random(256) + "," + random(256) + ")";
}
function random (integer) {
  return Math.floor(integer * Math.random())
}
export { drawCheckerBoard, newElement };

Second, here is the associated lisp code. This is the on-change functions of three button widgets on a dialog in a test project, where the first function draws a checker board, the second creates an HTML element in the dialog, and the third creates an HTML element on the CG screen. You could try this out by adding a form window to a new project, placing three button widgets in the upper-right area of the form window, and giving the button widgets these on-change functions. Also add a file named one.js to the project folder and put the JS code above into that file, and enter ("one.js") in the JavaScript Files to import widget on the CG/JS tab of the Project Manager dialog. Then run the project and generate the standalone app!

(defun draw-button-on-change (widget new-value old-value)
  (declare (ignorable widget new-value old-value))
  (call-js `((fun "drawCheckerBoard")
             (args ,(list (handle (parent widget)))))
           "one") ;; the pathname-name of the JS file
  t)
(defun new-sibling-button-on-change (widget new-value old-value)
  (declare (ignore new-value old-value))
  (new-element (handle (parent widget))
               (left widget)
               (+ (bottom (find-sibling :top-level widget)) 24)
               (width widget)(width widget))
  t)
(defun new-top-level-on-change (widget new-value old-value)
  (declare (ignore widget new-value old-value))
  (new-element 0 ;; the screen's handle is always zero
               60 60 200 200)
  t)
(defun new-element (parent-handle left top width height)
  (call-js `((fun "newElement")
             (args ,(list parent-handle left top width height)))
           "one")
  t)

running-in-event-handler-on-gtk

[new CG macro]

(defmacro running-in-event-handler-on-gtk (function &rest args)

This macro could be used around code that might get run in GTK mode and that might get called in a process other than the *single-cg-event-handling-process*. In that case, the call will get handed off to the *single-cg-event-handling-process*, as needed in GTK mode for thread safety, and the usual returned values will get passed back to the calling process and returned.

This would not be needed in typical applications where everything is initiated with interactive user gestures such as mouse clicks and keystrokes in widgets, because those events are all handled in the *single-cg-event-handling-process*. But it would be needed when calling CG functionality programmatically from outside the CG environment.

The function argument may be either a function name symbol or a lambda expression (but not a variable or other expression to evaluate to the function to call). Examples:

(running-in-event-handler-on-gtk + 1 2)

(running-in-event-handler-on-gtk
  (lambda (a b)(+ a b)) 1 2)

If you need to use this macro interactively a lot, such as by calling CG functions from a non-CG listener, then you could define your own macro on top of this one like this:

(defmacro now (function &rest args)
  `(running-in-event-handler-on-gtk ,function ,@args))

and then use that macro with less typing like this:

(now move-window my-window (make-position 300 400))

Background: Due to the way that GTK internally caches information on the function call stack, along with the fact that non-SMP lisps on the GTK platforms for CG (Linux and Mac) use multiple lightweight lisp threads in a single operating system thread that has a single function call stack, all GTK activity needs to run in the *single-cg-event-handling-process*.

show-parenthesis-matches-as-pop-up-window

[new CG function]

(defun show-parenthesis-matches-as-pop-up-window ()

CG calls this function to determine which way to show parenthesis-matching marks. In CG/JS mode this always returns true (because the alternative is not implementable), and elsewhere it returns the value of *show-parenthesis-matches-as-pop-up-window*, which an application could modify. There’s probably no reason for an application to call this function, except maybe to find out which style is being used.

virtual-screen-right

[newly-exported CG function]

(defun virtual-screen-right ()

Returns the rightmmost coordinate of all monitors in the system when called on Windows. On CG/JS and GTK this will always be the same as calling interior-width or exterior-width on the screen.

virtual-screen-bottom

[newly-exported CG function]

(defun virtual-screen-bottom ()

Returns the bottommmost coordinate of all monitors in the system when called on Windows. On CG/JS and GTK this will always be the same as calling interior-height or exterior-height on the screen.

numeric-string-p

[new CG utility function]

(defun numeric-string-p (string)

Attempts to return whether doing a lisp read on the specified string would return a number, rather than a symbol or other type of object. The returned boolean value (t or nil) may not be correct in all cases. Some CG applications use this to avoid interning symbols from unknown interactive user input when reading input as numbers, and so it may be useful elsewhere.

get-text-zoom

[new CG function]

(defmethod get-text-zoom ((window text-edit-pane))

This obscure function is implemented only on the Windows platform. It returns a rational number for the current zoom ratio of a text-edit-pane or rich-edit-pane. This will be 1 unless set-text-zoom has been used to change it, or perhaps if something like a special mouse wheel driver has changed it.

set-text-zoom

[new CG function]

(defmethod set-text-zoom ((window text-edit-pane) zoom-ratio)

This obscure function is implemented only on the Windows platform. It sets the zoom ratio of a text-edit-pane or rich-edit-pane. zoom-ratio should be a rational number between 1/64 and 64. The Microsoft control will adjust the size of the text by this factor.

*beep-loudness*, *beep-duration*, and *beep-frequency*

These variables affect the sound that’s produced when calling the function beep in web browser mode to emit a short beep, typically for a warning or error. *beep-loudness* goes up to 100 but that seems to be extremely loud and so the initial value is 12. *beep-duration* is the number of milliseconds that the sound will last, and the initial value is 80 (less than one-tenth of one second). *beep-frequency* is the pitch of the sound in herz (cycles per second), and the initial values is 440 (the A above middle C).

load-cg-patches

[new CG function]

(defun load-cg-patches ()

Loads all of the Common Graphics patches that have been downloaded and installed.

There is likely no need to call this function explicitly, because it is called automatically when saying (require :cg) to load all of Common Graphics into a development base lisp, and also when generating a CG application. In both cases it is called after loading the fasl files for each CG module; that’s important because loading a module’s fasl file after loading patches would override any patches to functions that are in that module.

One time that you may need to call this function is after loading individual CG modules into a base lisp, rather than loading all of CG. See also load-ide-patches.

Changes for Already-Existing CG Functions and Variables

user-close

[CG function with a new note]

If an application might get run in CG/JS mode, then any user-close method that prompts the user should first call web-browser-has-disconnected and not do the prompt if it returns true. Otherwise it could hang waiting on the disconnected browser to reply, preventing lisp from unwinding and exiting.

windowp

[CG function with a new note]

This function will always return nil if web-browser-has-disconnected returns true.

invalidate

[CG function with special CG/JS behavior in once case]

OLD: If box is nil, as it is by default, then the entire visible-box of the window is redrawn; otherwise …

NEW: If box is nil, as it is by default, then the entire visible-box of the window is redrawn (or the entire page-box in web browser mode if draws-the-entire-scrolling-page returns true). Otherwise …

line-height

[CG function with a new return-real parameter]

(defmethod line-height ((stream graphical-stream) &key return-real)

If return-real is true, then any non-integer real value is returned as-is, and otherwise it is rounded to an integer. When running in a web browser, JavaScript returns real values, and return-real can be used to avoid roundoff errors (especially when multiplying the line height).

screen-resolution-changed

[CG generic function with a new meaning in web browsers]

OLD: This function applies to the Microsoft Windows platform only.

NEW: This function is not called on the GTK platform.

[replacement for the paragraph beginning “This generic function is called whenever …”]

This generic function is called whenever a change is made to the screen resolution while the application is running. The screen resolution is the pixel coordinate range that fits onto the screen, and within which an application needs to fit its top-level windows.

An application should not call this function, but it may add methods for doing side effects such as moving or resizing application windows to fit the new screen resolution.

In Windows desktop mode, the screen resolution can change due to the user changing system-wide settings to scale everything by 125% (for example), or connecting via Remote Desktop from another machine, or swapping in a different monitor, or changing the effective resolution of the same monitor.

In web browser mode it, the logical screen is the interior of the browser tab where the app is running. A new screen resolution can then be due to the user resizing the whole browser window, or adding or removing a browser toolbar.

A special case in web browser mode is the browser’s commands to zoom (or scale) the whole web page, typically zooming in to read text more easily or zooming out to fit more content into view. To the CG application, zooming in looks just like the screen getting smaller, and zooming out looks just like the screen getting bigger. So the app can respond in the same way, without needing to know that it’s actually a zoom that was done. The usual browser keystrokes for zooming are in a project’s Browser Keychords property by default to let the browser handle them; those keystrokes are Control+Plus, Control+Minus, and Control+Zero, except substituting the Command key for the Control key on the Mac, plus the same keystrokes with the Shift key also held down..

get-line, nget-line, get-box, nget-box, get-fixed-box, and nget-fixed-box

[CG functions with an added note]

You can bind the variable *opaque-rubber-banding* to t around calls to this function to draw in a newer and nicer style than the traditional XORing, though it may be slower.

get-shape-line, nget-shape-line, get-shape-box, nget-shape-box, get-shape-fixed-box, and nget-shape-fixed-box

[CG functions with an added note]

You can bind the variable *opaque-rubber-banding* to t around calls to this function to draw in a newer and nicer style than the traditional XORing, though it may be slower. And this style may not work at all, depending on how the custom draw-fn and erase-fn are written.

mouse-wheel

[CG function with a new CG/JS note]

CG/JS note: When defining a custom mouse-wheel method for a window subclass, for CG/JS mode you will likely need to pass :grab-mouse-wheel t when calling make-window to create each instance of the subclass. This is not passed in all cases because it appears to also override scrolling with the trackpad.

stretch-mode

[CG function with different behavior in CG/JS mode]

CG/JS note: stretch-mode is re-interpreted in CG/JS mode, where JavaScript image smoothing is enabled when the value is :halftone, and not otherwise. This applies when stretching the pixmap either larger or smaller, rather than only when stretching it smaller as on Windows. The default is :color-on-color in CG/JS mode, and so image smoothing is NOT done by default (unlike the JavaScript default).

handle-scrolling-keys

[CG function with slightly different behavior in CG/JS mode]

After this sentence:

If the value is :page, then the PageUp and PageDown keys will scroll the window vertically by a page, or horizontally by a page if the control key is held down.

Add this new sentence:

Except in CG/JS mode the alt key is used instead of the control key, because most web browsers intercept Control+PageDown and Control+PageUp to switch to an adjacent browser tab.

pop-up-lettered-menu

[CG function with two additional parameters]

(defun pop-up-lettered-menu (choices &key (sortp t)
                                     (sort-predicate #'string<)
                                     (sort-key #'identity)
                                     (on-print nil)
                                     (stream (screen *system*))
                                     horizontal-justification
                                     vertical-justification
                                     position
                                     on-help)

horizontal-justification and vertical-justification are passed onward to pop-up-menu. See that function for the possible values.

ask-user-for-color

[CG function with two additional parameters]

(defun ask-user-for-color
    (&key initial-color (title "Select a Color")
          (prompt "Use the widgets to specify a new color.")
          show-custom-colors disable-custom-colors
          (stream (selected-window-or-screen)))

title should be a string to display in the title-bar of the dialog. This has no effect in Windows mode, where the Microsoft dialog does not provide for that.

prompt should be a string to display in the body of the dialog to instruct the user what to do. This is used only in CG/JS mode.

show-custom-colors and disable-custom-colors are used only in Windows mode.

ask-user-for-existing-pathname and ask-user-for-new-pathname

[CG functions with an additional parameter]

title: a string to display in the title-bar of the dialog. This is used only in CG/JS mode, due to the native Windows and GTK file dialogs not offering this.

ask-user-for-new-or-existing-directory

[CG function with three additional parameters]

select-file: whether the user can select a file or a directory. nil means that the user can select a directory only. :single means that the user can select one file. :multiple means that the user can select one or more files in the same directory (in which case the returned value is a list of pathnames rather than a single pathname).

When selecting a file, this dialog (written in CG) is an alternative to the standard dialog of the underlying windowing system that is shown by ask-user-for-existing-pathname and ask-user-for-new-pathname. In CG/JS mode, those functions always calls this one internally, due to the lack of a built-in dialog for selecting a file on the server machine.

default-extension: nil or a file type string like “txt”. This is used only when select-file is true and this extension is a string, in which case it will be added to the file path if a file name is typed without a file type.

initial-name: nil or a file name string that will be displayed initially in the widget for entering a file name.

[CG function with an additional start parameter]

(defun string-search (text-edit-pane string
                      &key backward start
                           case-sensitive (select t)
                           (lines-of-padding 0))

start: the character index at which the search will begin. If unspecified or nil, it will begin at the current text cursor position. If a range of text is selected, a forward search will begin by default at the end of the range, and a backward search will begin at the beginning of the range.

backward: [removing some phrases] if true, then the search is done toward the beginning of the buffer. Otherwise the search is done toward the end of the buffer. The default is nil, meaning do a forward search.

font-fixed-width-p

[CG function with two new paragraphs]

[for after the main (short) paragraph]

A more convenient function to use is font-face-fixed-width-p, which works directly on a font face name. The fontmetrics accessor functions like this one, on the other hand, require assigning a font to a window and then retrieving that window’s fontmetrics object. This function may be more efficient if the font is already assigned to a window.

[for the end]

GTK note: This function always returns nil on GTK, where the font metric information does not include a corresponding value. So font-face-fixed-width-p should always be used on GTK to determine whether a font is fixed width.

font-truetype-p

[CG function with a new note for the end]

This function is meaningful only on the Windows platform. The value will always be nil on other platforms.

font-vector-p

[CG function with a new note for the end]

This function is meaningful only on the Windows platform. The value will always be nil on other platforms.

font-device-p

[CG function with a new note for the end]

This function is meaningful only on the Windows platform. The value will always be nil on other platforms.

default-error-handler-for-delivery

[CG function with a new note]

After this sentence:

The default value is report-unexpected-error-and-exit, which displays a simple dialog about an unexpected error and then exits the application.

Insert this sentence:

An alternate value is write-backtrace-to-file, for debugging a generated application that is crashing or failing to start up.

*show-parenthesis-matches-as-pop-up-window*

[CG variable with a difference for CG/JS]

CG/JS note: This variable is not used in CG/JS mode. CG actually calls the function show-parenthesis-matches-as-pop-up-window to determine which style to use, and that function always returns true in CG/JS mode because only the pop-up-window style is possible.

*parenthesis-match-pop-up-milliseconds*

[CG variable with a new initial value.]

The initial value has changed from 1000 to 2000, to give the user more time to spot the parenthesis match mark before it goes away.

parenthesis-matching-style

[CG setfable generic function with a CG/JS limitation]

This option is not supported in CG/JS mode, where the :color-block style is always used. parenthesis-matching-color is supported though.

single-item-list and multi-item-list

[CG widgets with a fix]

The “Scrollbars” paragraph near the bottom of the doc pages for these two widgets can go away due to a fix. Previously, the horizontal scroll bar would never appear unless the multi-column-p property is used. And that limitation was only on the Windows platform.

tooltip

[CG function with a new cross-reference]

A lower-level function for showing a tooltip at an arbitrary time and place is show-tooltip-explicitly.

hide-tooltip

[CG function with a doc tweak]

OLD: This function is not normally called by an application

NEW: This function would normally be called by an application only to hide a tooltip that was shown by calling show-tooltip-explicitly

process-pending-events

[CG function with doc changes]

New paragraph for after the first one:

In particular, if you have a busy loop (typically for a dragging operation) that stops looping when a function like key-is-down-p or mouse-button-state indicates a user gesture for exiting the loop, then you likely need to call this function inside the loop to handle events that are needed by those functions. This is not needed in Windows mode, but is needed in CG/JS mode and GTK mode, so code that had run only in Windows mode may need this change to also run in CG/JS mode.

See also *use-alternate-websocket-nudge* if your custom dragging loop is not as smooth as you would like.

OLD: If your application runs on platforms where Allegro threads are not native operating system threads, then you should probably call process-pending-events-if-event-handler instead of this function.

NEW: If your application may run in GTK mode, then it should call process-pending-events-if-event-handler instead of this one. That function is fine to always call instead of this one.

process-pending-events-if-event-handler

[CG function with a replacement description]

This is a safer alternative to process-pending-events for code that might run in GTK mode in a process other than the *single-cg-event-handling-process*. It simply calls process-pending-events in the usual case, but it does nothing when called in that special case where events should not be processed.

border

[CG generic function with a new possible value]

:none-allowing-title: use no border, but still allow the window to have a title-bar. Otherwise a borderless window will never have a title-bar.

make-window

[CG generic function with an undocumented argument]

:border: the style of the border (or frame) of the window. This mostly determines the border thickness, and sometimes the color. See the border property for the possible values.

label

[CG function with a replacement description]

(defgeneric label (object)

Returns a short string that is suitable for displaying to the user for the specified object, or nil when not applicable.

For a static-text widget, this calls value. For other widgets, this calls title. (Those other exported functions could be called instead of this one, but this one is handy for calling on any dialog-item.)

For a tab-info of a tab-control, a header-info of a header-control, or a header-cell of a grid-widget, this returns the string that is displayed in the object, and the setf of this function can be called to make the object display a different string.

fill-texture

[The following paragraph should say “Windows Only:” at the beginning. This does not work on CG/JS mode or GTK mode.]

As an alternative to specifying a custom pixmap, any of several special built-in fill-textures may be used by specifying a particular keyword symbol rather than a pixmap. These symbols include :12%foreground, :25%foreground, …

fill-texture-origin

[CG function with a new paragraph]

CG/JS Note: Setting this origin currently has no effect in CG/JS mode. But it’s probably not important because the JavaScript case doesn’t have the problem of offsetting the texture differently according to how far the window is scrolled, leading to misaligned drawing when a scroll draws only the part that just got scrolled into view.

outline-item-selected-background-color

[CG function with a change in default method behavior]

OLD: returns the color that is returned by a call to system-highlight-background-color.

NEW: returns the color that is returned by (make-rgb :red 128 :green 240 :blue 255).

printer-names

[CG function with a new note]

Printing hardcopy has been implemented only on Windows, and this function will return nil in CG/JS mode and GTK mode.

[CG variable with a new cross-reference]

In CG/JS mode, the variable *modal-dialogs-disable-owner-js* is used instead of this one.

*toolbar-icon-height*

[CG variable with replacement description]

The height of toolbars that are created by add-toolbar when running in Windows mode or GTK mode. The initial value is 22 on Windows, but 28 on GTK to leave extra room for taller GTK button widgets that hard-code a large internal margin. This variable could be modified at startup time to make all toolbars be a different height, though the function toolbar-icon-height should be called to return the height in any mode.

monitor-info

[CG function with a new note for CG/JS]

CG/JS notes: Multiple monitors do not pertain to CG/JS, where the whole universe is one web browser tab. So this always returns just one inner list for one monitor, where the acting screen size is the interior size of the browser tab. This will change, though, when the user zooms (scales) the whole application, to reflect the logical screen size that is being scaled to fit into the browser tab’s interior.

push-lisp-clipboard and pop-lisp-clipboard

[CG functions with a new note]

A simpler function for dealing directly with the system clipboard (when you don’t care about the lisp clipboard stack) is clipboard-object.

palette

[CG function with a replacement for the “GTK Note”]

Palettes have been implemented only for Windows mode. They will not be implemented for CG/JS mode or GTK mode because they are basically obsolete.

click-to-toggle-inclusion

[CG function with a new cross-reference]

An application could implement side effects when an object is toggled on or off by supplying an [`exclude-or-include-chart-object’] method.

use-single-cg-event-handling-process

[new CG function that replaces a global variable]

(defun use-single-cg-event-handling-process ()

This function replaces the former global variable *use-single-cg-event-handling-process*, due to how a single application executable can now run in either CG/JS mode or desktop mode. Here is the replacement description.

This function returns true in GTK mode and nil elsewhere. Apps probably do not need to call this function, though this function officially returns true when process-pending-events-if-event-handler needs to be used instead of process-pending-events. But since process-pending-events-if-event-handler works in all cases, it’s simpler to always use it in any app that might run in GTK mode.

Related note: The single event-handling process that’s used with GTK must never call process-wait. See cg-process-wait.

beep

[CG function with a new note for CG/JS]

When running in CG/JS mode, the variables *beep-loudness*, *beep-duration*, and *beep-frequency* affect the sound that is produced.

New IDE Functions and Variables

run-in-web-browser

[new IDE setfable generic function]

(defgeneric run-in-web-browser (ide-configuration)

Returns whether the IDE will run in a web browser on successive runs, rather than as a desktop Windows or GTK program. This is typically set on the IDE 1 tab of the Tools | Options dialog, but it could be set programmatically like this:

(setf (ide:run-in-web-browser (configuration ide:*ide-system*)) t)

web-browser-style-options

[new IDE setfable generic function]

(defgeneric web-browser-style-options (ide-configuration)

Returns a set of stylistic options for use in the IDE, such as window border colors and thicknesses. These values will override the default values for all CG/JS apps that are defined in *cgjs-client-options*, to give the IDE itself a custom style.

You could edit the values interactively by using Tools | Inspect System Data | IDE Configuration Options, then scroll down to web-browser-style-options near the bottom of the list of all IDE options in the inspector. Clicking on the value reveals a small button on the right, and clicking on that small button will show a multi-line text editor for editing a copy of *cgjs-client-options* that’s used for the IDE itself. You will need to restart the IDE after editing these options for them to take effect.

To set the values programmatically instead, use a form like this one:

(setf (ide:web-browser-style-options
        (cg:configuration ide:*ide-system*))
  ...a subset of *cgjs-client-options* with custom values...
)

As with the custom options for a project, the IDE’s values can be any subset of *cgjs-client-options*, rather than the entire list that you see in the inspector.

These options are saved in a file with a name like allegro-style-options.txt, where allegro is the file name of the particular ACL executable that you’re running. The file will be saved in the user’s home directory, on Windows meaning their personal Documents directory. This file could be deleted to restore the IDE to use the default stylistic options for all CG/JS applications.

*ide-is-in-js-mode*

[new IDE variable]

The value of this variable will be true in the IDE if the IDE itself is running in a web browser, or nil if it is running as a standalone desktop Windows or GTK application.

exit-ide

[new IDE function]

(defun exit-ide ()

Exits the IDE without exiting lisp. The IDE’s windows will be closed and the IDE’s processes will exit.

start-ide could later be called to run the IDE again in the same lisp session.

handle-home-key-shortcuts-in-ide-outlines

[new IDE setfable generic function]

(defmethod handle-home-key-shortcuts-in-ide-outlines (ide-configuration)

Whether all of the outline widgets in the IDE will use the handle-home-key-shortcuts option to make the J and K keys move to the next or previous item, and the F and D keys open and close the current item. When true, this allows navigating an outline widget without reaching for the arrow keys. But it also conflicts with the ability to jump to an item by typing the first one or more of its characters, and so this option is off by default. In earlier releases this behavior was hard-wired on in the IDE.

save-whether-to-show-subproject-modules

[new IDE setfable generic function and project property]

(defmethod save-whether-to-show-subproject-modules (project)

When this setfable project property is true, a saved project will remember which subprojects were currently showing their modules in the Modules tab of the Project Manager, and restore that state in the Project Manager when the project is opened again. The default value is t.

This property could be turned off to avoid saving a modified version of the project’s .lpr file after simply showing or hiding a subproject’s modules in the Project Manager, when no functional changes have been made to the project. This may be useful for source code management. You can find this property by using the Inspect Project button in the Project Manager’s toolbar.

include-dead-locals-in-backtraces

[new IDE generic function]

(defmethod include-dead-locals-in-backtraces (ide-configuration)

Returns (or sets with setf) whether backtraces will show the values of local variables that are dead. The default value is nil because dead local variables are typically not of interest. This option is not on the Options dialog, but it could be set interactively with “Tools | Inspect System Data | IDE Configuration Options”, or set programmatically like this:

(setf (ide:include-dead-locals-in-backtraces
        (configuration ide:*ide-system*))
      t)

The values of dead locals will probably be interesting only if you first say (excl::set-process-debug [some-process] t) on the process that breaks, and then run the test that breaks. Otherwise the garbage collector will turn dead locals into non-lisp objects.

[new IDE setfable generic function]

(defmethod print-generate-application-call (ide-configuration)

Returns (or sets with setf) whether generating a standalone application will print the call to generate-application or build-lisp-image. The default value is nil, but you could turn on this option to check whether all of the arguments were passed as intended. This applies when using the interactive commands “Build Project Executable” and “Build Project Distribution” on the IDE’s File menu, and also when calling ide:build-project programmatically.

cgjs-options

[new IDE generic function]

(defmethod cgjs-options ((project project))

Returns a cgjs-options object that stores CG/JS server options for a project. These are typically set interactively in The CG/JS Tab of the Project Manager in the IDE, but this object can be used for reading or setting them programmatically.

run-as-web-browser-server

A CG/JS option that is typically set with the Run as Web Browser Server widget in The CG/JS Tab of the Project Manager in the IDE. But it could be set programmatically like this:

(setf (ide:run-as-web-browser-server
        (cg:cgjs-options (ide:current-project)))
      t)

start-local-client

A CG/JS option that is typically set with the Start Local Client widget in The CG/JS Tab of the Project Manager in the IDE. But it could be set programmatically like this:

(setf (ide:start-local-client
        (cg:cgjs-options (ide:current-project)))
      t)

exit-server-on-client-exit

A CG/JS option that is typically set with the Exit Server on Client Exit widget in The CG/JS Tab of the Project Manager in the IDE. But it could be set programmatically like this:

(setf (ide:exit-server-on-client-exit
        (cg:cgjs-options (ide:current-project)))
      t)

confirm-exit

A CG/JS option that is typically set with the Confirm Exit widget in The CG/JS Tab of the Project Manager in the IDE.

disallow-running-in-non-default-mode

A CG/JS option that is typically set with the Disallow Running In Non-Default Mode widget in The CG/JS Tab of the Project Manager in the IDE.

limit-connections-to-same-machine

A CG/JS option that is typically set with the Limit Connections to Same Machine widget in The CG/JS Tab of the Project Manager in the IDE.

show-cgjs-server-window

A CG/JS option that is typically set with the Show CG/JS Server Window widget in The CG/JS Tab of the Project Manager in the IDE.

cgjs-logging

A CG/JS option that is typically set with the Show CG/JS Logging Window widget in The CG/JS Tab of the Project Manager in the IDE.

title-for-browser-tab

A CG/JS option that is typically set with the Title for Browser Tab widget in The CG/JS Tab of the Project Manager in the IDE.

browser-keychords

A CG/JS option that is typically set with the Browser Keychords widget in The CG/JS Tab of the Project Manager in the IDE.

browser-server-port

A CG/JS option that is typically set with the Browser Server Port widget in The CG/JS Tab of the Project Manager in the IDE.

max-clients

A CG/JS option that is typically set with the Max Clients widget in The CG/JS Tab of the Project Manager in the IDE.

style-options

A CG/JS option that is typically set with the Style Options widget in The CG/JS Tab of the Project Manager in the IDE.

javascript-files-to-import

A CG/JS option that is typically set with the JavaScript Files to Import widget in The CG/JS Tab of the Project Manager in the IDE.

include-modules-for-starting-local-client

A CG/JS option that is typically set with the Include Modules for Starting Local Client widget in The CG/JS Tab of the Project Manager in the IDE.

include-modules-for-cgjs-logging

A CG/JS option that is typically set with the Include Modules for CG/JS Logging widget in The CG/JS Tab of the Project Manager in the IDE.

query-web-browser-exit

A CG/JS option that determines whether the web browser will prompt for exit confirmation when the user tries to close the browser tab of the IDE or reload the page. Setting this to true can avoid losing unsaved changes in the IDE, such as in the editor. The default value is true, especially because Control+F4 is often used for closing a single application window, but when running in a web browser it will close the browser tab and thereby exit the IDE. Setting this option calls the setf of the CG function confirm-web-browser-exit. This option has an effect only in web browser mode.

This option is in addition to the query-exit option for all platforms for whether to prompt for confirmation when generally exiting the IDE.

load-ide-patches

[new IDE function]

(defun load-ide-patches ()

Loads all of the IDE patches that have been downloaded and installed.

There is likely no need to call this function explicitly, because it is called automatically when saying (require :ide) to load the IDE into a development base lisp. It is called after loading the fasl files for each IDE module; that’s important because loading a module’s fasl file after loading patches would override any patches to functions that are in that module. See also load-cg-patches.

Changes for Already-Existing IDE Functions and Dialogs

start-ide

[IDE function with new keyword parameters]

(defun start-ide (&key run-in-web-browser
                       (start-local-client t))

If run-in-web-browser is true, then the IDE will start up in the default web browser, and otherwise it will start up as a desktop Windows or GTK program.

If start-local-client is true (as it is by default), then the IDE will appear in the default web browser on the same machine. Otherwise a web browser on another machine (or the same one) could connect to the server machine at the port that’s printed by start-ide, to display the IDE there instead.

query-exit

[IDE user option with a new cross-reference for CG/JS mode]

When running the IDE in a web browser, see also query-web-browser-exit for confirmation when closing the browser tab for the IDE.

The Project Manager Modules Tab

[new comment for doc/ide-menus-and-dialogs/project-manager-dialog-modules.htm]

In addition to the distributed files in the list of modules, two additional files will always be distributed for CG/JS mode. The content of these files could be modified if needed in the distribution for a particular application. One file is browser-not-supported.html, which will be shown if the user tries to run the app in a web browser that’s not supported by CG/JS, though Internet Explorer is the only known common browser that is no longer supported. The other file is ssl-not-found.html which will be shown if OpenSSL is not found when trying to run the app in a web browser. CG/JS communicates with the web browser using the WebSocket protocol, which requires SSL.

The Trace Dialog

[new comment for doc/ide-menus-and-dialogs/trace-dialog.htm]

OLD: Next to the name is the amount of time spent in the call.

NEW: Next to the function name is the number of milliseconds that were spent in the call. After that is a phrase like “after 7 ms”. That means that the call was entered 7 milliseconds after the most recent entrance to or exit from a traced function. These notes may help determine what is taking more time than expected.

The Association List of Global Style Options

*cgjs-client-options*

[new CG variable]

The value of this variable is an association list of stylistic options such as the color and thickness of window borders, the font used in all menus, and the delay before showing a child menu. These are typically system-wide parameters that affect all applications in a windowing system, but there’s no equivalent in the web browser world and so CG defines them itself in this alist.

The function cgjs-client-option could be used to return any of these values, such as (cgjs-client-option :browser-keychords). The setf could be used to modify this alist, though that will have no effect after CG has already started up and sent the values to the web browser, and so special means are provided for specifying custom values.

To modify these style options for a project, use the Style Options widget on The CG/JS Tab of the Project Manager in the IDE. Or programmatically set the ide:style-options of the cg:cgjs-options of the project.

To modify these style options for the IDE, see web-browser-style-options.

Here is the actual code for this variable, with comments that explain each option.

;; Options that affect how the web browser client works, and which
;; are passed over to the web browser for the JavaScript code to use.
;; These are mostly stylistic things, such as the color and thickness
;; of window borders and the font to use in all menus.

(copy-tree
 `(

   ;; ------------------------------------------------------------
   ;; This first set (most of them) gets passed to the web browser
   ;; just once at startup time.
   ;; ------------------------------------------------------------

   ;; Keystrokes that should get passed to the web browser for its
   ;; default behavior.  Each keystroke should contain a main key name
   ;; that's preceded by zero or more shift key names, with a plus
   ;; character between names.  The possible shift key names are
   ;; Shift, Control, and Alt, plus Meta on the Mac (for the Command
   ;; key).  When the the main key is a letter key, it should be the
   ;; uppercase character.  For numeral and punctuation keys, it
   ;; should be the unshifted character.  Other special key names
   ;; (which come from JavaScript) include Enter, Escape, Backspace,
   ;; Tab, Home, End, PageUp, PageDown, ArrowLeft, ArrowUp,
   ;; ArrowRight, ArrowDown, Insert, Delete, F1 through F12,
   ;; Shift, Control, Alt, PrintScreen, and OS.  (OS is for the
   ;; Windows/Start key on a Windows keyboard.)  These names are all
   ;; case-sensitive.
   ;; This one can be specified as the ide:browser-keychords
   ;; property of the cgjs-options of a project.
   (:browser-keychords 
    ("F11"                 ;; toggling full-screen mode
     "Control+Meta+F"      ;; full-screen mode on the Mac
     ;; zooming (scaling) the whole app
     "Control+0" "Control+-" "Control+=" "Control+Shift+-" "Control+Shift+="
     ;; zooming keys on Mac; Meta is the Command key
     "Meta+0" "Meta+-" "Meta+=" "Meta+Shift+-" "Meta+Shift+="))

   ;; The text to appear in the small tab above the application.
   ;; This should be a lisp string, or nil to use a project property.
   ;; This one can be specified as the ide:title-for-browser-tab
   ;; property of the cgjs-options of a project.
   (:title-for-browser-tab nil)

   ;; The characters at which draw-string-in-box can wrap text
   ;; to a new line.
   (:string-in-box-wrapping-delimiters (#\space))

   ;; Whether scroll bars will appear on the logical screen, which
   ;; is the web browser interior, whenever top-level windows exist
   ;; partly or fully beyond the width or height of the logical screen.
   ;; (When this is turned on, currently there's a problem in both
   ;; Firefox and Chromium when the app window is maximized and the user
   ;; drags the browser size smaller, where occasionally the browser will
   ;; needlessly turn on the scrollbars of the screen, causing them
   ;; to cover part of the application window on the right and bottom.)
   (:scrolling-screen nil)

   ;; Whether a shadow effect will be drawn below and to the right of
   ;; all windows that have borders.
   (:show-box-shadows t)

   ;; The color of the logical screen, which is the web browser interior.
   (:screen-background-color
    ,(make-rgb :red #xdd :green #xdd :blue #xdd))

   ;; The default background color for all windows of class
   ;; dialog-mixin.  This value is returned by the function
   ;; system-dialog-background-color.
   (:dialog-background-color
    ,(make-rgb :red #xf0 :green #xf0 :blue #xf0))

   ;; The default background color for all tab-control widgets.
   (:tab-control-background-color
    ,(make-rgb :red #xf8 :green #xf8 :blue #xf8))

   ;; The text color that's used in widgets and menu-items whose
   ;; "available" property is nil, indicating that they will not
   ;; respond.  This value is also returned by the function
   ;; system-disabled-color.
   (:unavailable-text-color
    ,(make-rgb :red #x77 :green #x77 :blue #x77))

   ;; The colors that are used for the borders and title text
   ;; of a window when the window is either the selected child
   ;; of its parent or not.
   (:selected-window-title-color ;; the text color
    ,(make-rgb :red #x00 :green #x00 :blue #x00))
   (:unselected-window-title-color
    ,(make-rgb :red #x55 :green #x55 :blue #x55))
   (:selected-window-border-color
    ,(make-rgb :red #x70 :green #x98 :blue #xff)
    #+previous ;; a bolder cyan/blue
    ,(make-rgb :red #x00 :green #xaa :blue #xff))
   (:unselected-window-border-color
    ,(make-rgb :red #x88 :green #xb0 :blue #xf0)
    #+previous ;; a bolder cyan/blue
    ,(make-rgb :red #x77 :green #xcc :blue #xff))
   (:selected-widget-border-color
    ,(make-rgb :red #x00 :green #x00 :blue #xcc))
   (:unselected-widget-border-color
    ,(make-rgb :red #xaa :green #xbb :blue #xcc))

   ;; The colors that are used in a thin outer edge of a window frame,
   ;; partly to provide a 3D look and also to clarify where the frames
   ;; of multiple windows overlap.  These colors are returned by the
   ;; functions system-edge-highlight-color and
   ;; system-edge-shadow-color.
   (:frame-highlight-color
    ,(make-rgb :red #xbb :green #xee :blue #xff)
    #+previous
    ,(make-rgb :red #x88 :green #xdd :blue #xff))
   (:frame-lowlight-color
    ,(make-rgb :green #x88 :blue #xaa))

   ;; The height in pixels of every menu-bar.  It should be big
   ;; enough for the current menu-bar font (below).
   (:menu-bar-height 24)

   ;; The font to use in every menu-bar.
   (:menu-bar-font-face "sans-serif")
   (:menu-bar-font-size 14)

   ;; The background-color of every menu-bar.
   (:menu-bar-color ,(make-rgb :red #xc7 :green #xdd :blue #xee))

   ;; The font to use in every menu.  This should be a JavaScript font name.
   (:menu-font-face "sans-serif")
   (:menu-font-size 13)

   ;; The number of pixels of vertical and horizontal blank space
   ;; around the text within each item in a menu or item-list widget.
   (:menu-item-vpadding 3)
   (:menu-item-hpadding 3)
   (:list-item-vpadding 2)
   (:list-item-hpadding 4)

   ;; The radius in pixels of the corners of button widgets.
   ;; nil means not rounded at all.
   (:button-corner-radius nil)

   ;; The background-color for an item in a menu or item-list widget
   ;; when the item is highlighted by moving the mouse over it (for
   ;; a menu) or using the arrow keys.
   (:menu-item-highlighted-color
    ,(make-rgb :red #x88 :green #xcc :blue #xff))
   (:list-item-highlighted-color
    ,(make-rgb :red #xcf :green #xdf :blue #xef))

   ;; The number of milliseconds to pause before showing a child menu
   ;; after the mouse is moved to that child menu's title in the
   ;; parent menu.  This avoids showing child menus as you quickly
   ;; move the mouse over their titles, especially because they might
   ;; cover the parent menu when the browser window is not very wide.
   ;; If the value is zero, then there is no delay.
   (:child-menu-delay 500) ;; bug26409

   ;; -------------------------------------------------------------
   ;; The rest are passed later whenever particular CG functions
   ;; such as make-window are called, rather than being initialized
   ;; just once at startup time.
   ;; -------------------------------------------------------------

   ;; These are used for any window whose border property is :frame,
   ;; :plain, or :dialog-box, where the first one is used when the
   ;; resizable property is true, and the second when it is nil.
   (:resizing-border-width 8)
   (:plain-border-width 2)

   ;; Used when the border property is :palette.
   (:palette-border-width 2)

   ;; Used when the border property is :static or :black.
   (:static-border-width 1)

   ;; The pixel height of window title bars (not including the
   ;; window's border).  (The height of the font for the title
   ;; will be automatically calculated to fit this title bar
   ;; height, and the sans-serif JavaScript font is always used.)
   (:title-height 19)

   ;; The amount by which the font-size of a stream's current
   ;; font will be multiplied to derive the line-height of the
   ;; stream, which determines how far apart lines of text get
   ;; printed onto the stream.  The default factor of 1.2 will
   ;; leave empty space between text lines that's one-fifth
   ;; as tall as the font characters.  This affects the built-in
   ;; line spacing of a text-edit-pane, and the printing of
   ;; newlines in regular windows.
   (:line-height-factor 1.2)

   ;; These are for the similarly-named CG functions.  Those
   ;; functions will return the values that are established here.
   (:system-foreground-color ,black)
   (:system-background-color ,white)
   (:system-highlight-foreground-color ,black)
   (:system-highlight-background-color 
    ,(make-rgb :red 96 :green 232 :blue 255))
   (:system-disabled-color
    ,(make-rgb :red 109 :green 109 :blue 109 :alpha 255))
   (:system-app-workspace-color
    ,(make-rgb :red 171 :green 171 :blue 171 :alpha 255))

   ;; ---------------------------------
   ;; The rest are probably not useful.
   ;; ---------------------------------

   ;; Whether to use the native JavaScript combo-box (which
   ;; wasn't fully working out) rather than one that's constructed
   ;; from more basic HTML elements.
   (:use-native-combo-box nil)

   ;; The interval in seconds between custom pongs that the CGJS
   ;; client will send to help determine when a client may have
   ;; died, though other techniques now appear to be sufficient.
   ;; Zero means that the client will never send custom pongs
   ;; at all (which requires a constant timer).
   (:pong-interval 0))))

An Example Web Page for Embedding a CG/JS Application

Here is a complete HTML file that shows how to embed a CG/JS application in a web page, if that application is running with the --launcher option.

<!-- -*- mode: js; js-indent-level: 2; indent-tabs-mode: nil; -*- -->
<!DOCTYPE html><html lang="en"><head>
<title>Remotely Launching an Embedded Common Graphics Application</title>
<meta charset="utf-8"/></head>
<script>

// This file demonstrates launching a private copy of a remote Common
// Graphics (CG) application, and embedding that application in a web
// page.  In this example, a button widget launches the app and
// displays it in an HTML iframe element, and the JavaScript code here
// can be adapted for your own web site.

// A CG feature allows a single "launcher" instance of an app to be
// running as a server and listening for requests from web browsers.
// It will launch a separate instance of the app for each web page
// that requests one, up to a specified limit.  (It can optionally use
// the launcher instance itself for one client, to minimize the number
// of executables that are running.)

// The launcher instance of the CG app should already be running at a
// known machine and port.  The command line for running the launcher
// instance would look something like this (where the launcher will
// listen on port 9009):

// my-cg-app.exe -b yes -l no -x no -o 9009 --launcher yes

// -b yes           run in web browser mode rather than desktop mode
// -l no            do not start a local client in a web browser
// -x no            do not exit the server when a client exits
// -o 9009          listen for new clients on port 9009
// --launcher yes   launch another instance when remotely requested

// Something like a button widget in a web page can then run a script
// that sends an HTTP request to that machine at port 9009 to request
// an instance of the CG app.  The reply to that request will be a
// port on the same machine for a private instance of the app that was
// started up for that client.  The script can then tell an iframe or
// a new browser tab to visit the machine at that port to display its
// instance of the CG app.

// Some of the finer points below are not really necessary, such as
// making it work to directly restart the CG app when the maximum
// number of clients are currently running, but those things are
// included in case you want to use them.

// Set these variables as needed for your web page or application.

// The machine where the CG app is running as a server.
let host = "localhost";
// The port where the CG app is running as a server.
let launcherPort = 9009;
// A password can be required with a command line argument
// like "--remote-password secret".  Otherwise this can be null.
let password = "secret";
// The ID of the iframe in which to embed the CG app.
let cgFrameName = "cgFrame";
// Use null instead to show the CG app in a new browser tab.
// let cgFrameName = null;

// These will get set during the run.
let ourPort, cgFrame, messageWidget;

// The onload function that's called when this file gets loaded.
function exampleOnLoad () {
  // Cache some global variables.
  // The HTML element where this example displays status messages.
  messageWidget = document.getElementById("messageWidget");
  // The iframe (if any) where we will display the CG app.
  if (cgFrameName) {
    cgFrame = document.getElementById(cgFrameName);
    // When reloading the web page, this avoids automatically
    // reloading the iframe as well, which would leave ourPort
    // undefined (for example), and restarting the CG app at that time
    // is probably not desirable anyway.  After a reload, the user can
    // run the CG app again if desired.
    cgFrame.src = "";
  }
  // This would prompt the user when they try to close the web browser
  // tab or reload the web page, asking if they really want to exit.
  // That can be useful to avoid losing unsaved changes in the CG app.
  // window.onbeforeunload = function (event) {
  //   event.preventDefault();   // the official way
  //   return "Really exit?";    // the traditonal way
  // }
}
// Code for the "Run the App" button.  This function sends an HTTP
// request to the launcher application, asking for a personal instance
// of the application executable (or to use the launcher instance
// itself if available).
function runTheApp () {
  // If the app is already running, then first tell it to exit and
  // wait for that to complete, and then run the app again.  This is a
  // fine point that makes it work even when the maximum number of
  // clients are currently running.  If that's not important, then
  // just call runTheAppNow directly.
  if (ourPort) exitTheApp(true); // true means to run after exiting
  else runTheAppNow();
}
function runTheAppNow (restarting) {
  let args = {};
  if (password) args.password = password;
  if (restarting) args.restarting = true;
  statusMessage("Launching a copy of the CG/JS app at "
                + host + ":" + launcherPort + " ...");
  // Ask the launcher that's always running at a known port to launch
  // a copy of itself for us to use at another port. When the reply is
  // received asynchronously, displayTheApp will be called.  This
  // general sendHttpRequest utility function is down below.
  sendHttpRequest (host, launcherPort, "launch", args, displayTheApp);
}
// This function is called when a reply is received for the launch
// request above that requests an instance of the application.  The
// reply contains the port for our private application instance.
function displayTheApp (status, response) {
  // Handle various failure cases for the launch request, and
  // otherwise display the application.
  if (status === 0)
    statusMessage("There apparently is no CG application running at "
                  + host + ":" + launcherPort + ".");
  else if (status === 200) {
    if (response === "0") // CG's code for refusing a client
      statusMessage("The CG server would not launch an instance, "
                    + "probably because the maximum number of "
                    + "clients are now running.");
    else if (response === "-1") // CG's code for a bad password
      statusMessage("Bad password.");
    else {
      // In the normal case, tell the iframe to connect to the server
      // machine at our personal port, to display the app.
      ourPort = response; // cache our private port
      let url = "http://" + host + ":" + ourPort;
      if (cgFrame) cgFrame.src = url;
      // If no cgFrame was specified, then use a new browser tab.
      else open(url, "_blank");
      statusMessage("Success!  Our copy of the application "
                    + "is starting up at port " + ourPort + ".");
    }
  }
  // Possible other failures.
  else statusMessage("Launching the application failed with status "
                     + status + ".");
}
// Code for the the "Exit the App" button.  This function sends an
// HTTP message to this client's own instance of the application, to
// exit that executable (unless it's the launcher).  This is not
// really necessary because the executable will reliably exit when the
// browser tab is closed or the web page is reloaded, or the
// application is exited from within.  But calling this function
// before launching the application again avoids a problem where it
// doesn't work when the maximum number of clients are currently
// running.  And when the launcher instance is being used, doing this
// before relaunching will allow the launcher instance to be used
// once again, which is faster.
function exitTheApp (runAfterExiting) {
  if (ourPort) {
    statusMessage("Exiting the CG/JS app at "
                  + host + ":" + ourPort + "...");
    let arguments = password && { password: password };
    sendHttpRequest (host, ourPort, "exit", arguments,
                     afterExiting, runAfterExiting);
  }
  else statusMessage("There is no running application to exit.")
}
// This is called when the reply is received to the exit request
// above.  It can then restart the app when that was requested.
function afterExiting (status, response, runAfterExiting) {
  if (status === 200) {
    statusMessage("Success!  The application running at port "
                  + ourPort + " has exited.");
    // When the "Run the App" button first exits the app,
    // run the app now that the previous run has exited.
    if (runAfterExiting) runTheAppNow(true);
  }
  else statusMessage("Exiting the application failed with status "
                     + status + ".");
  ourPort = null;
}
// Sending any HTTP message and handling the reply.  This is a general
// utility function that you could use without modification.  It sends
// an HTTP request to the CG app and sets up a function to be called
// when a reply is received.  command is a command name like "launch"
// that the app published.  arguments is a JavaScript object to send
// with the request as URL query arguments.  replyFunction will get
// called when the reply is received, with the status and response
// of the reply as the first two arguments and replyFunctionArguments
// as a third argument
function sendHttpRequest (host, port, command, arguments,
                          replyFunction, replyFunctionArguments) {
  let request = new window.XMLHttpRequest();
  let url = "http://" + host + ":" + port + "/" + command;
  // Translate the "arguments" object into a URL query string.  For
  // example, { password: "secret" } would turn into ?password=secret
  // (or &password=secret for later arguments) in the URL.  Each
  // argument value will be uri-encoded (percent-encoded) to escape
  // URL syntax characers.
  if (arguments) {
    let first = true;
    for (let key in arguments) {
      url = url + (first ? "?" : "&") + key + "=" +
        encodeURIComponent(arguments[key]);
      first = false;
    }
  }
  // When the reply to the request is received asynchronously, call
  // the replyFunction on the status and the response that were
  // received, plus the arbitrary replyFunctionArguments.
  request.onreadystatechange = function () {
    if (request.readyState === XMLHttpRequest.DONE)
      (replyFunction (request.status, request.response,
                      replyFunctionArguments));
  }
  // Send the request and return immediately.
  request.open("GET", url);
  request.send();
}
function statusMessage (string) {
  messageWidget.innerText = string;
}
</script>
<body onload="exampleOnLoad()"
      style="background: #FFFCF4;
             margin-left: 1%; margin-right: 2%; margin-top: 20px;
             font-family: sans-serif; font-size: 14pt">

<h2>Remotely Launching an Embedded Common Graphics Application</h2>

<p>If you are running a CG application as a server as shown below
in web browser mode (-b yes),
without starting a local client in a web browser (-l no),
without exiting the server when a client exits (-x no),
connecting to the CG server at port 9009 (-o 9009),
and acting as a launcher to run an instance of the app for each
client (up to a limit) that requests one (--launcher yes),
then pressing the Run the App button will tell the (invisible)
iframe element to display the app in this web page.</p>

<pre><b>my-cg-app.exe -b yes -l no -x no -o 9009 --launcher yes</b></pre>

Using -x no allows the server instance to keep running while various
users connect to it and disconnect, though it also means that you
will need to use some operating system facility to kill the server
eventually.
</p>
<button type="button" id="runTheApp" onclick="runTheApp()"
        style="font-family: sans-serif; font-size: 14pt">
Run the App</button>
<button type="button" id="exitTheApp" onclick="exitTheApp()"
        style="font-family: sans-serif; font-size: 14pt">
Exit the App</button>
<br><br>
<div id="messageWidget">
Status messages will appear here.</div><br>
<iframe id="cgFrame"
        style="height: 700px; width: 90%; border: none">
</iframe>
</body></html>

Release Notes

Here are the notable changes in recent updates to the CG/JS download. Newer updates can be downloaded from the CG/JS download web page.

2022-09-21

Fixed: When using the IDE’s Run Project command in CG/JS mode, in certain cases the CG/JS client for the running project could erroneously attempt to call JavaScript code on IDE windows, leading to confusion or even corruption.

Fixed: Dragging a split-bar widget often moved the split-bar by only a small amount or not at all, unless you continued holding the mouse button down briefly after dragging to the desired final position.

Fixed: Resizing a window interactively by dragging its border was very confused in Firefox on Windows 11.

Fixed: The default menu and title bar font sizes were too big in CG/JS. (And these are somewhat awkward to change with the style-options of a project or with the web-browser-style-options of the IDE.)

Fixed: The function get-pixmap was recently broken in Windows and GTK modes. It could also fail in certain cases in CG/JS mode (which was a different bug).

Fixed: In the 2022-07-01 CG/JS release, the IDE’s grapher (used by “Tools | Graph Subclasses” and runtime analyzer graphs) was messed up in Windows and GTK modes, due to enhancements for CG/JS mode.

The new JavaScript Files to Import widget on The CG/JS Tab of the Project Manager in the IDE can be used to specify a list of custom JavaScript files that will be loaded into a CG/JS app at run time. Or you can call the new CG function import-javascript-file directly instead. Then the app can call exported JavaScript functions by calling the new CG function call-js. This enables possibilities such as making pre-existing JavaScript drawing code draw in a CG window, or mixing trees of custom HTML elements with trees of CG windows.

The new Disallow Running In Non-Default Mode check box on The CG/JS Tab of the Project Manager in the IDE can be used if you intend to support only web browser mode or only desktop mode, to prevent users from running in the other mode with a --run-as-web-browser-server or -b command line argument.

The custom file selection dialog for CG/JS has a few additional features that are typical of file dialogs. It now allows you to type a path that’s relative to the currently selected folder, and then press the Enter key or the OK button to select that folder in the outline and list the files in it. You can also type *.foo (for example) to list only the files of type foo. And you no longer need to type a period after the name of a file that has no type.

Hovering over the error message widget in an IDE backtrace pane now shows the full error message in a tooltip. This alleviates the problem with the short widget being rather cramped for longer error messages (though it is scrollable). The restarts dialog is also now somewhat taller to show more of the error message without scrolling it.

The new IDE configuration option print-generate-application-call controls whether generating a standalone application will print the call to generate-application or build-lisp-image, for debugging purposes. Prior to ACL 11.0 the call was always printed, but the default value of this new option is nil.

Fixed: When tracing functions that are called in a running project (in CG/JS mode), sometimes a series of traced calls could show the same incremental call count.

Fixed: A scrolling-static-text widget could not be scrolled with the vertical arrow keys and the paging keys (when present) in CG/JS mode.

Fixed: Reindenting a longer definition in the IDE in Chromium (but not Firefox) hung the IDE for up to several seconds due to handling a flood of redundant notifications from the web browser. (This was after the general reindentation speedups from early 2022.)

2022-07-01

Fixed: Generating standalone applications with File | Build Project Distribution was broken in some cases. And running the generated application still has a known problem on the Mac with finding OpenSSL in the application folder; for now, in a terminal you could visit the folder where the app was generated, and run it with a line like the following (except changing “foo” to the name of your executable file):

LD_LIBRARY_PATH=. ./foo

The IDE’s trace dialog can now collect any number of traced calls, rather than being limited to about 1600 before it would reach the maximum height of a JavaScript canvas (in CG/JS mode). It also now collects many calls much faster, and most of the speed optimizations affect all outline widgets.

Printing to the IDE’s listener by using a series of calls to printing functions (rather than one or a few calls) is now around three times as fast. (This is still a relatively slow aspect of the IDE due to many messages between server and client.) These optimizations may also help in the IDE’s editor and other text-editing widgets.

Other optimizations affect all drawing by avoiding sending drawing calls to the web browser when it is clear that JavaScript would end up clipping them anyway.

Fixed: There were problems with defining pixmaps at load time rather than at run time, such as when a defvar calls make-instance on the pixmap class, or when there are picture widgets on a form window for designing a dialog interactively.

The new window and widget property small-canvas-mode is useful for very large scrolling pages (especially when they exceed the maximum size of a JavaScript canvas), and may be preferable for other reasons in some cases. It is now used by the outline widget in the IDE’s trace dialog.

Animating a chart-widget by changing property values in a loop (such as by calling set-chart-value repeatedly) now draws the frames at a more even rate in CG/JS mode, rather than drawing several quickly in a clump and then pausing a couple of seconds before the next clump. This is done by calling request-a-dummy-reply-from-web-browser internally, which applications can also do if needed.

Fixed: Saving a file in the IDE’s editor (or calling save-file) did not specify an external format, and on Windows defaulted to saving with the :latin1 external format rather than :utf-8 as intended. Files did get loaded with :utf-8, though, leading to a mismatch on Windows for non-ASCII characters.

Fixed: Telling move-window-behind to move a window behind itself killed lisp in CG/JS mode. This happened internally when using Help | Quick Symbol Info in an IDE backtrace pane.

Fixed: In CG/JS mode, the IDE’s Search | Replace command did nothing when the replacement string is the null string. This was also true in calls to the CG function string-replace.

Fixed: Sometimes a scroll bar could appear when resizing a window when the scroll bar is not needed. Sometimes that could cover content, such as the last item in an outline widget when the horizontal scroll bar appears when it is not needed.

Fixed: The CG/JS IDE or a user application could get into a bad state if it tried to display an invalid string that contains any unpaired lisp surrogate characters for an extended Unicode character (one that does not fit into 16 bits).

Fixed: The parenthesis-matching mark in the IDE’s editor did not appear at certain times when it should.

Fixed: Runtime Analyzer graphs were slow to scroll and to select a node, and they could stop scrolling after resizing the window (until you select a node). The selected node did not contrast sufficiently for spotting it easily, and all nodes did not contrast well with the background.

Fixed: IDE tool windows did not return to their earlier positions and sizes after resizing the screen and back while the IDE is running. In CG/JS mode this includes times like maximizing and unmaximizing the web browser, or showing the JavaScript debugging console and then hiding it again.

Fixed: Tracing individual methods in the Definitions dialog did not work when the source code location is not known.

Fixed: Pull-down menu titles used a default browser font rather than the intended CG font, and were especially huge in Opera.

Fixed: The long versions of command-line options had no effect in a case-insensitive-upper lisp (like the Express release) due to looking for the uppercase symbol name in the arguments.

Fixed: If a timeout occurs while waiting for a reply from the web browser, confusion resulted due to the wrong reply being sent to a later request. (A timeout can still result in something not getting done, though ordinarily it should not happen.)

Fixed: In CG/JS mode, draw-bezier-curve and fill-bezier-curve worked when the vertices argument is a vector, but failed when it is a list.

Fixed: A dropping outline that’s on a modal dialog was not showing its dropping pane in CG/JS mode.

The new make-window initarg :no-canvas-mode can be passed as true to avoid creating a JavaScript backing store canvas for the window, to save memory. CG now uses that for any frame-with-single-child parent window, because its interior is always completely covered by its built-in child window.

The new function write-backtrace-to-file is an alternate value for the default-error-handler-for-delivery option of a project, for debugging a generated application that crashes or fails to start up.

A standalone scrollbar widget that’s disabled will now draw in a different way to show that it’s disabled.

Fixed: When inspecting a string that contains extended Unicode characters in the CG/JS IDE, each lisp surrogate character will now say something like “high surrogate 55357”, rather than breaking.

Fixed: Programmatically setting the value of a check-box or radio-button to a true value other than t did not draw the widget in the selected state in CG/JS mode.

Fixed: In certain cases the default fixed-width font could get saved with a tiny size in the IDE’s options file.

As a CG/JS application is starting up, the string “Loading …” will appear in the web browser until the first window gets shown.

2022-02-10

When running in a web browser, using the IDE command “Edit | Reindent” to reindent definitions in the editor is now several times faster. And there are no more annoying intermediate redisplays that had drawn each line of code as it gets reindented and flashed temporarily selected text and briefly scrolled away and back. The editor will also not briefly scroll away and back when reindenting a single line (such as by pressing TAB or Enter) when the text cursor is near the top or bottom of the window, or when using “File | Revert to Saved”.

Protected Windows system directories are no longer listed in the file selection dialog when running in a web browser on Windows.

2022-01-31

The 64-bit Linux platform previously still required GTK to be installed. It is now like the 32-bit Linux and is fully GTK-less and completely browser based.

Fixed: The IDE’s inspector was bogus for inspecting a closure (possibly leading to corruption), and you could not inspect a macro object at all.

Fixed: A button in a multi-picture-button did not draw correctly after being pressed in some scenarios in CG/JS mode.

Fixed: In Firefox, typing Enter in the first line of text of a text-edit-pane inserted the newline one character too early. And selecting all of the text and then cutting the selected text did not remove the text.

Typing Alt+S (for example) to move the keyboard focus to the widget whose label widget has S as its underlined access key did not move to the correct widget when there are more than one hidden widgets between those two widgets.

The function font-face-fixed-width-p has now been exported and documented.

Fixed: Setting a menu-item title to a string that contains certain special characters like double-quotes or backslashes did not change the title, due to not escaping the characters as needed when passing them to JavaScript. CG/JS only. (This fix appears to have been in the previous release, but was missed in the release notes.)

2021-12-14

New audio-player and video-player widgets in CG/JS mode allow placing the standard HTML audio and video controls into a CG app, and to either control them programmatically or allow the user to control them interactively.

Fixed: In the previous release, remotely launching instances of an embedded CG/JS application failed after the first instance. This was an old bug that was exposed by a recent correct fix.

Fixed: In a CG/JS IDE, running a project and tracing functions that are called in the running project could trigger odd effects such as writing a string that’s intended for the trace dialog into a random widget of the running project.

Fixed: When using the text search and replace commands on the IDE’s Search menu in CG/JS mode, the combo-box widgets that hold the search and replace strings sometimes initially appeared blank rather than showing the default values that they are holding. This symptom may have been specific to Firefox, but it was a general CG/JS bug.

Fixed: ask-user-for-new-pathname on CG/JS did not show the confirmation dialog when the warn-if-exists-p argument is true and there is already a file at the path that the user selects.

Fixed: In an embedded CG/JS app, if the keyboard focus is in the app but then you click in the web page outside the app and then click back on the same widget in the app that had the focus before, then keyboard shortcuts would not work until you click some other widget or the app’s title bar.

Fixed: Selecting the next value in an item-list or list-view widget by typing one or more of the leading characeters of that value worked (in most cases) only on Windows. It is now implemented for CG/JS and CG on GTK as well. And for an item-list it always found the first match from the top, rather than the next match after the selected value, as documented. Also, the default value for select-on-multiple-characters is now true rather than nil, and the default value for select-on-multiple-characters-time-limit is now 1000 (one second) rather than 2000.

The new CG function list-widget-focus-index returns the index of the item in an item-list or list-view widget that currently has the keyboard focus. This is not necessarily the same item as the selected value. The setf of this function can be called to move the focus to a different item without also changing the selected value.

The new IDE option handle-home-key-shortcuts-in-ide-outlines allows toggling the handle-home-key-shortcuts property of all IDE outline widgets at once. The default value is nil, even though that’s the opposite of the previous behavior, because it conflicts with the ability to jump to an item by typing the first one or more of its characters, which is now uniformly in effect in all item-list, list-view, and outline widgets in the IDE.

Fixed: When using the IDE’s Find In Files dialog in CG/JS mode, the repeated status bar messages and additional matching files that it shows during a search often did not appear until the end of the search, making it unclear whether anything was still happening, and not showing how far the search has progressed through the directory tree.

The new function request-a-dummy-reply-from-web-browser may be useful for forcing the web browser to refresh the display in certain cases where it otherwise delays refreshing while it is busy handling many requests from lisp.

Fixed: Clicking on a chart-widget could break if you have turned on the click-to-toggle-inclusion option and the chart has only a single chart-object.

Fixed: The outline widget option select-on-typing was not defined as a property, and so did not appear with other widget properties in the inspector.

Fixed: In a CG/JS IDE, opening a project was slowed down because selecting each loaded module in the project manager’s list also updates the toolbar buttons, which was very slow.

Fixed: Editing text in a grid-widget cell in CG/JS mode allowed the HTML edit control to draw its focus rectangle, which was redundant with the grid cell border and therefore somewhat ugly.

Fixed: When calling set-focus-cell on the first subsection of a grid-section when that section had not yet had the focus, application-added methods of set-focus-cell did not get called for side effects.

Fixed: Setting read-base to something other than 10 broke CG/JS.

2021-09-13

Fixed: Exiting the CG/JS IDE by closing its web browser tab while there are unsaved changes in the editor could result in an empty source code file for the unsaved editor buffer. It would then need to be restored from its *.~cl file.

Fixed: On the Mac, the IDE’s menu commands did not use the usual keystrokes that include the Command key. And when running the IDE in a web browser, this also caused pasting and undoing in text-edit-panes not to work, due to a mismatch with the browser’s native keystrokes that must be used for those operations (Command+V and Command+Z).

Fixed: The multi-picture-button widget, such as in the IDE’s toolbars, usually did not display its button images in the Safari web browser. And in all web browsers, clicking the scroll gadget on the end of a multi-picture-button (when present) did not redraw the widget to show the change until you moved the mouse over the widget’s buttons.

Fixed: If you ran a project (with the IDE in CG/JS mode), where the project allows only a single client, and then exited the running app in the app’s official way before closing the browser tab for the project run, then the IDE would disallow running the project again. In that case it did not decrement the number of currently running clients for the app, and thought that you were exceeding the maximum number of allowed clients.

Fixed: In a particular case when using the Find In Files dialog, the keyboard focus would end up in an editor buffer when CG believes it is still in the search string widget of the Find In Files dialog. This would lead to confusion when trying to type into the search string widget. (CG/JS mode only.)

Fixed: Calling add-item to add items one at a time to a combo-box or item-list would sometimes insert a new item in the wrong order. This affects history combo-boxes in the IDE like the search string widget in the Find In Files dialog and the “Search | Find Forward” dialog. That could be confusing when using the down arrow key to select the previous value, where in some cases the up arrow was needed instead to find the value. (CG/JS mode only.)

Fixed: Selecting a different radio-button menu-item did not clear the check mark from the previously-selected menu-item in the same radio-button cluster. (CG/JS mode only.)

Fixed: The file selection dialog did not scroll the list of file names to reveal the initially selected file.

Fixed: Typing Alt+N (for example) in an outline widget would jump to the next outline item that begins with N, even after invoking the pull-down menu whose access character is N. Now only N or Shift+N (for example) will do the jump to another outline item, as intended. (CG/JS mode only.)

Fixed: The parts of a typable combo-box did not fit together correctly, and could clip the right end of the text by several pixels when text fills the text area. (CG/JS mode only.)

Partially fixed: Sometimes starting up the IDE in the Safari web browser would leave the toolbar, main background area, and status bar scooted upward by 18 pixels, leaving the toolbar partially clipped off the top. This appears to be a Safari quirk, and the best workaround that we’ve found is that moving the mouse cursor over any of those windows after startup will now scoot them all to where they should be.

The new functions displaying-on-mac and control-or-command-key may be useful for things such as using the Command key in keyboard shortcuts on the Mac only.

The file selection dialog in web browser mode on Windows will now include a “My Folders” logical folder for more easily locating files in other logical folders such as the Downloads or Dropbox folder.

Fixed: Resizing a static-picture, picture-button, or up-down-control larger (such as on a form window) failed to draw the image beyond the original size of the widget. (CG/JS mode only.)

Fixed: The thin highlight edge of a window frame did not draw on rounded frame borders. (CG/JS mode only.)

Fixed: Using the Common Lisp function dribble to capture a listener session in a file removed all newlines in the file that’s created. (CG/JS mode only.)

The new function tell-web-browser-app-is-exiting might be useful in atypical use cases.

2021-08-13

Minor installation change only.

2021-07-23

On the Mac, the IDE with CG/JS installed will now always run in web browser mode, and never in desktop mode on GTK. This removes the need to have GTK and the XQuartz X11 server installed. Standalone applications that are generated from projects in a CG/JS IDE will similarly run only in web browser mode.

When the --browser-server-port option is a range of ports like 9009-9012, and the --launcher facility is also used, then any launched instances of the application will now be restricted to that same range of ports.

Fixed: Confusion occasionally resulted when receiving replies from the web browser, though the problems had been seen only in SMP lisps.

Fixed: Typing Control+H in a text-edit-pane (such as the IDE’s editor and listener) was erasing two characters instead of one in CG/JS mode.

Fixed: Calling load-file in CG/JS mode to load a text file into a text-edit-pane (such as the IDE’s editor) will now read the file with the :utf-8 external format as intended.

Fixed: The IDE’s Find Definitions (in all modes) didn’t find the source file (and index within the file) of methods that have EQL specializers for non-keyword symbols.

When using the new --launcher facility, a launched instance of a CG/JS app will now receive the app’s command line arguments that were passed the the launcher. And a launch HTTP command from a web page can now pass a command-line-args argument.

Only a single --file-to-publish command line argument will now be handled.

2021-06-25

Any CG/JS app can now serve as an application server for itself. The new --launcher command line argument makes a CG/JS app launch a copy of itself whenever it receives a request from a web page that will embed a personal instance of the app. The new --remote-password argument (or the CGJS_REMOTE_PASSWORD environment variable) can be used with that for security. The new --file-to-publish argument allows publishing an arbitrary web page, such as a demo that embeds a CG/JS app. A web page that launches and embeds an app can also send an exit HTTP request to its own instance to exit that lisp.

The new Confirm Exit project option makes the web browser prompt for exit confirmation when you try to close the browser tab of a CG/JS app or reload the web page. This can avoid accidentally losing unsaved changes in a CG/JS app. This can be specified with the new Confirm Exit check box on the CG/JS tab of the project manager, or with a --confirm-exit command-line argument, or while the app is running if it calls the setf of confirm-web-browser-exit. For the IDE, the new option query-web-browser-exit does the same.

Fixed: The short versions of some CG/JS command line arguments were ignored.

Fixed: Firefox version 89 broke cutting and indenting in text-editing widgets. Multi-level undo and redo now work well in that Firefox version.

Common Graphics and the IDE on GTK now work in SMP lisps.

Fixed: When using the rubber-banding functions like get-line in web browser mode, if you scroll during the drag then the rubber-banding line would not get drawn in the portion of the window contents that were newly scrolled into view. Also, when passing the scroll-p argument to these functions for automatic scrolling as you drag off of the window, the scrolling will now be faster in both web browser mode and desktop mode.

The value of the --cgjs-logging command line argument can now be a file path, to write the information into a file instead of the logging window.

The function beep now works in CG/JS mode. The new variables *beep-loudness*, *beep-duration*, and *beep-frequency* control the sound in CG/JS mode.

2021-06-02

Mouse wheel fixes were made for CG/JS mode, especially for the chart-widget.

The new chart-widget function effective-value-axis-range returns that range that the widget calculated.

Fixed: Sometimes a second click was required on a button widget in Safari.

The CG/JS documentation has been filled out.

IDE: Fixed: Running a project while Start Local Client is unchecked in the CG/JS tab of the Project Manager did not show the running project in a browser tab.

2021-05-13

Fixed confusion when typing Enter or TAB on the last line of an editor buffer.

2021-05-11

initial release