{"id":15717638,"url":"https://github.com/vitovan/calm","last_synced_at":"2025-04-06T05:16:12.712Z","repository":{"id":60759029,"uuid":"543547002","full_name":"VitoVan/calm","owner":"VitoVan","description":"Calm down and draw something, in Lisp.","archived":false,"fork":false,"pushed_at":"2024-12-20T01:48:22.000Z","size":50508,"stargazers_count":110,"open_issues_count":8,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-30T04:08:48.240Z","etag":null,"topics":["appimage","cairo","calm","gui","lisp","macos-app","sdl2","windows-installer"],"latest_commit_sha":null,"homepage":"https://vitovan.com/calm/","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VitoVan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"VitoVan","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2022-09-30T10:29:19.000Z","updated_at":"2025-03-30T00:49:50.000Z","dependencies_parsed_at":"2023-11-16T07:29:23.848Z","dependency_job_id":"f4b3a884-b17a-4b5e-9934-6daf23774a1f","html_url":"https://github.com/VitoVan/calm","commit_stats":{"total_commits":282,"total_committers":4,"mean_commits":70.5,"dds":0.2730496453900709,"last_synced_commit":"ed5ddff8f264efe92fab661da733466a7835a5f3"},"previous_names":[],"tags_count":167,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VitoVan%2Fcalm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VitoVan%2Fcalm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VitoVan%2Fcalm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VitoVan%2Fcalm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VitoVan","download_url":"https://codeload.github.com/VitoVan/calm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247436286,"owners_count":20938533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["appimage","cairo","calm","gui","lisp","macos-app","sdl2","windows-installer"],"created_at":"2024-10-03T21:50:50.058Z","updated_at":"2025-04-06T05:16:12.680Z","avatar_url":"https://github.com/VitoVan.png","language":"Common Lisp","funding_links":["https://github.com/sponsors/VitoVan","https://github.com/sponsors/VitoVan/"],"categories":[],"sub_categories":[],"readme":"# \u003cimg style=\"vertical-align:middle;margin-right:10px;\" width=\"100\" alt=\"Calm\" src=\"docs/images/calm.png\"\u003e C A L M\n\n[![CI](https://github.com/VitoVan/calm/actions/workflows/calm.yml/badge.svg)](https://github.com/VitoVan/calm/actions/workflows/calm.yml) [![GitHub all releases](https://img.shields.io/github/downloads/vitovan/calm/total?color=brightgreen\u0026label=Downloads\u0026style=flat)](#pre-built-binary) [![Discord](https://img.shields.io/discord/1124894601908584528?logo=discord\u0026logoColor=white\u0026label=Chat\u0026labelColor=%235865f2\u0026color=white)](https://discord.gg/xN6VeaMr2a)\n\n**C**anvas **A**ided **L**isp **M**agic: Create canvas-based applications with Lisp and distribute them on Linux, macOS, Windows, and the web.\n\nEnglish | [日本語](README_JA.md)\n\n## Hello, World!\n\nFind whatever directory, and create a file: **canvas.lisp**\n\n```lisp\n(defparameter *color-list* '((0.83 0.82 0.84) (0.89 0.12 0.17) (0.94 0.87 0.47) (0 0.35 0.59)))\n(defun draw ()\n  (c:set-operator :darken)\n  (dotimes (i 7)\n    (c:arc (+ 72 (* (- (/ *calm-window-width* 5) 44) i)) 73 50 0 (* 2 pi))\n    (apply #'c:set-source-rgb (nth (if (\u003e= i 4) (- i 4) i) *color-list*))\n    (c:fill-path)))\n```\n\nLaunch your terminal, cd to that directory, and enter the command:\n\n```bash\ncalm\n```\n\n[![Hello World](docs/examples/circles/canvas.png)](#hello-world)\n\n## Examples\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://vitovan.com/calm/1.3.2/fan/calm.html\"\u003e\u003cimg width=\"250\" alt=\"Fan\" src=\"./docs/examples/fan/canvas.png\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://vitovan.com/calm/1.3.2/mondrian/calm.html\"\u003e\u003cimg width=\"250\" alt=\"Mondrian\" src=\"./docs/examples/mondrian/canvas.png\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://vitovan.com/calm/1.3.2/meditator/calm.html\"\u003e\u003cimg width=\"250\" alt=\"Meditator\" src=\"./docs/examples/meditator/canvas.png\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nSource files and binaries for the above examples are [here](https://github.com/VitoVan/calm/tree/main/docs/examples) and [here](https://github.com/VitoVan/calm/releases/tag/1.3.2).\n\nFor more applications made with CALM, please check [Made with CALM](https://github.com/VitoVan/made-with-calm).\n\n## \u003cimg style=\"vertical-align:middle;margin-right:5px;\" width=\"50\" alt=\"Calm Installer\" src=\"./docs/images/calm.png\"\u003e Installation\n\n### Pre-built Binary\n\n1. Downloads\n\n   [![Linux Download](https://img.shields.io/badge/Linux-glibc%202.31+-FFD032.svg?logo=linux)](\u003chttps://github.com/VitoVan/calm/releases/latest/download/calm.tgz\u003e)\n   [![macOS Sonoma Download](https://img.shields.io/badge/macOS-Sonoma-black?logo=apple)](\u003chttps://github.com/VitoVan/calm/releases/latest/download/calm.macos-14.dmg\u003e)\n   [![Windows Download](https://img.shields.io/badge/Windows-Windows%2010/11-017fd5.svg?logo=windows)](\u003chttps://github.com/VitoVan/calm/releases/latest/download/calm.zip\u003e)\n   [![macOS Ventura Download](https://img.shields.io/badge/macOS-Ventura-white?logo=apple)](\u003chttps://github.com/VitoVan/calm/releases/latest/download/calm.macos-13.dmg\u003e)\n   [![macOS Monterey Download](https://img.shields.io/badge/macOS-Monterey-white?logo=apple)](\u003chttps://github.com/VitoVan/calm/releases/latest/download/calm.macos-12.dmg\u003e)\n\n2. Extract\n\n3. Add the extracted directory into your PATH environment\n\n   for macOS, add `/Applications/Calm.app/Contents/MacOS/` instead\n\nFor macOS and Windows users, you need to be smarter than [Windows SmartScreen](https://www.google.com/search?q=how+to+get+around+windows+smartscreen) or able to [tame macOS](https://www.google.com/search?q=how+to+remove+quarantine+flags+in+macos) to use CALM. In case anything went wrong, here is an [Installation Guide](docs/installation.md).\n\n### Run from Source\n\nSupported platforms are currently limited by [Github Actions runner images](https://github.com/actions/runner-images).\n\nIf your platforms are not supported, feel free to [Run from Source](docs/hacking.md#run-from-source).\n\n## \u003cimg style=\"vertical-align:middle;margin-right:5px;\" width=\"50\" alt=\"Calm Application\" src=\"./docs/images/app.png\"\u003e Distribution\n\nLaunch your terminal, cd to the directory where the file **canvas.lisp** exists, enter the command:\n\n```bash\ncalm publish\n```\n\nThis command will generate different packages on different platforms:\n\n**Linux: AppImage**\n\n![Linux AppImage](./docs/images/linux-appimage.png)\n\n\u003e  **Note**\n\u003e\n\u003e  The fancy window icon doesn't show on Wayland, I don't know why.\n\n**macOS: Application Bundle**\n\n![macOS Application DMG](./docs/images/macos-dmg.png)\n\n\u003e**Note**\n\u003e\n\u003eDMG creation is powered by [create-dmg](https://github.com/create-dmg/create-dmg) and will be installed by `brew install create-dmg` if it were not present. So if you don't have create-dmg, this will install create-dmg for you.\n\u003e\n\u003eAnd, if you don't have [Homebrew](https://brew.sh/), this will also install Homebrew for you.\n\u003e\n\u003eThe binary detection was done by `command -v create-dmg` and `command -v brew`.\n\n**Windows: Installer**\n\n![Windows Installer](./docs/images/windows-installer.png)\n\n\u003e **Note**\n\u003e\n\u003e Installer creation is powered by [NSIS](https://nsis.sourceforge.io/) and will be installed by `winget install nsis` if it were not present. So if you don't have NSIS (i.e., `makensis`) under your PATH, this will install NSIS for you.\n\u003e\n\u003e And, if you don't have [winget](https://github.com/microsoft/winget-cli) under your PATH, this will also install winget for you.\n\u003e\n\u003e The binary detection was done by `where makensis` and `where winget`.\n\n### To the Web\n\n```bash\ncalm publish-web\n```\n\nThis command could compile your Lisp code into web pages that could be served on the internet.\n\nFor more, please refer to the [Command Reference](#command-reference).\n\n# CALM - References\n\nFrom CALM 1.0.0, the version number will follow [Semantic Versioning Specification](https://semver.org/spec/v2.0.0.html). This means you can use CALM calmly without worrying about me being crazy. Because whenever I'm going to be crazy, I will let you know [before anything got changed](https://semver.org/spec/v2.0.0.html#spec-item-7) and bump the major version if [anything could surprise](https://semver.org/spec/v2.0.0.html#spec-item-8) you.\n\nKeep CALM and have fun.\n\n## Command Reference\n\n### `calm`\n\nYou should run this command inside your project directory, where the file **canvas.lisp** should exist.\n\nThis command will load **canvas.lisp** and show a window according to the instructions of the function  `draw` or `draw-forever`. The file **canvas.lisp** is just a regular Lisp source file, you do whatever you like in it.\n\nFor CALM-related functions and parameters, please refer to the [API Reference](#api-reference).\n\n### `calm hello`\n\nThis command will create a sample application with the default directory structure. You should create a project directory first, such as:\n\n```bash\nmkdir my-cool-app\ncd my-cool-app\ncalm hello\n```\n\nYou will have the following files and directories created:\n\n```text\n.\n├── assets\n├── canvas.lisp\n└── fonts\n    └── fonts.conf\n```\n\nFiles put into **assets** and **fonts** directories will be packed with your application during distribution. If you put your favorite font into the **fonts** directory, you will be able to use it inside your application.\n\nFor more about font usage, please refer to the [API Reference: Rendering Text](#rendering-text).\n\n### `calm publish`\n\nThis command will generate:\n\n- Linux AppImage\n- macOS Application Bundle inside DMG\n- Windows Application Installer\n\naccording to the platform it was running on.\n\nIt does not take any arguments, but some options could be set through the environment variables, please check `calm publish-with-options` for the option details.\n\n### `calm publish-with-options`\n\nThis command will do the same thing as `calm publish`, instead it will ask your opinions on all the customizable options (with a default value provided, don't worry), respectively:\n\n| OS | ENV | Description                                                  |\n| ---------------- | -------------------- | ------------------------------------------------------------ |\n| Linux            | APP_NAME             | The name of the AppImage file                                |\n| Linux            | APP_ICON             | Icon of the AppImage file and SDL2 Window, absolute path of a PNG file |\n| macOS            | APP_NAME             | The name of the macOS Application bundle, will appear in the Launchpad |\n| macOS            | APP_ICON             | macOS Application icon, absolute path of an ICNS file        |\n| macOS            | BUNDLE_ID            | [CFBundleIdentifier](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier), such as `com.vitovan.helloworld` |\n| macOS            | APP_VERSION          | [CFBundleShortVersionString](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring), such as: `10.14.1` |\n| macOS            | DMG_ICON             | The icon of the Apple Disk Image (DMG), absolute path of an ICNS file |\n| Windows          | APP_NAME             | Windows Application Name, will appear in the *Control Panel \u003e Programs and Features*, *Apps \u0026 features* and as the name of desktop shortcut |\n| Windows          | APP_ICON             | Windows EXE icon, absolute path of an ICO file               |\n\nIf you have provided the corresponding environment variable, the option will not be asked. You could also set these environment variables while using the command `calm publish`, the options will be picked up.\n\n### `calm publish-web`\n\nThis command will generate a  **web** directory containing all the necessary materials for you to serve it on the internet. The common usage could be like this:\n\n```bash\ncd my-cool-app\ncalm publish-web\n```\n\nYou will have the following files created:\n\n```text\nweb\n├── calm.data\n├── calm.html\n├── calm.js\n├── calm.wasm\n├── canvas.js\n├── favicon.ico\n└── jscl.js\n```\n\n**calm.html** is the entry point, due to the limitation of the web browser, please view this file through HTTP protocol, such as:\n\n```bash\ncd web\npython3 -m http.server 8000\n```\n\nThen open http://127.0.0.1:8000/calm.html in your browser.\n\nNote: The files inside **fonts** and **assets** directories will *NOT* be packed by default, please check the REBUILD_WASM_P option below.\n\n### `calm publish-web-with-options`\n\nThis command works like `calm publish-with-options` except it's for `calm publish-web`.\n\n| ENV | Description                                                  |\n| -------------------- | ------------------------------------------------------------ |\n| LISP_FILES           | Code like `(load \"shape.lisp\")` may cause problems for the web application, since JSCL will try to load that file via HTTP requests. \u003cbr/\u003eIf you need to include the extra files other than **canvas.lisp**, please modify your code to bypass JSCL, for example: `#-jscl (load \"shape.lisp\")` and then set this option, such as: `(\"/abs/path/to/canvas.lisp\" \"/abs/path/to/shape.lisp\")`. Please remember to escape the double quotes if you're going to set the ENV. |\n| REBUILD_WASM_P       | By default WebAssembly files were not built locally, they were downloaded from the [CALM Releases](https://github.com/VitoVan/calm/releases/): **calm.tar**. This prebuilt WebAssembly binary bundled with [OpenSan-Regular.ttf](https://github.com/googlefonts/opensans/raw/main/fonts/ttf/OpenSans-Regular.ttf) and [exposed all the Cairo and SDL2 APIs](https://github.com/VitoVan/calm/blob/main/s/usr/web/wasm.lisp#L61) mentioned in the below [API Reference](#api-reference) section.\u003cbr/\u003e If you need to bundle other **fonts** or **assets**, or you need to expose more C APIs be exposed to the web, please set this option to \"yes\". \u003cbr/\u003eCaution: Building WebAssembly binaries involves a whole lot of dependencies, to simplify this progress, I irresponsibly utilized docker. So, please make sure you have the `docker` command at your disposal.\u003cbr/\u003eDefault: \"no\" |\n\nSince JSCL is the backbone of CALM on the web, any change of JSCL will be considered as a change of CALM itself. The code base of JSCL used by each version of CALM is fixed, it won't change unless you update CALM. Please feel safe to use it.\n\n### API Reference\n\nCALM is intended to be a thin layer above [SDL2](https://wiki.libsdl.org/SDL2/FrontPage), [Cairo](https://www.cairographics.org/), and some other things. So the number of APIs provided by CALM is intended to be as small as possible.\n\n#### Fundamentals\n\n##### File `canvas.lisp`\n\nThis is the entry file for a CALM application. Typically, it should contain a function called `draw`.\n\n##### Function `draw`\n\nThis is the entry function for a CALM application, it will be called once the application started. You are supposed to call some canvas drawing functions to be shown, such as:\n\n```lisp\n(defun draw ()\n  (c:set-source-rgb 1 0 0)\n  (c:arc 200 73 50 0 (* 2 pi))\n  (c:fill-path))\n```\n\nThis function will be called passively. That is to say, this function won't be called again after the first call, unless any event has been triggered by the user, such as mouse motion, key down, key up, mouse button down, etc.\n\nIf you want to continually refresh the canvas without user interaction, you should use `draw-forever`.\n\nNote: Functions like `c:arc` are third-party APIs exposed by CALM. Please refer to [Drawing on Canvas](#drawing-on-canvas) for more info.\n\n##### Function `draw-forever`\n\nThis function also serves as the entry point for a CALM application, similar to the `draw` function. It is important to avoid defining both `draw` and `draw-forever` as doing so would have severe consequences, comparable to killing John Wick's dog.\n\nThis function will be called every `*calm-delay*` milliseconds, regardless of user interaction.\n\n##### Variable `*calm-delay*`\n\nThis variable controls how many milliseconds CALM should wait before refreshing the canvas.\n\nDefault: 42\n\nThis variable only works on the desktop platform, for the web platform, please check `*calm-fps*`.\n\n##### Variable `*calm-redraw*`\n\nThis variable controls if the canvas will be refreshed from now on.\n\nNormally, you don't need to touch this variable. But if you are using `draw-forever` and you want to manually control the process of refreshing, it could be useful. Such as:\n\n```lisp\n(defparameter *game-started* nil)\n\n(defun on-keyup (key)\n  (when (c:keq key :SCANCODE-SPACE)\n    (setf *game-started* (not *game-started*))))\n\n(defun draw-forever ()\n  (format t \"drawing canvas...~%\")\n  (c:set-source-rgb (/ 12 255) (/ 55 255) (/ 132 255))\n  (c:paint)\n  (c:set-source-rgb 1 1 1)\n  (c:move-to 70 90)\n  (c:select-font-family \"Arial\" :normal :normal)\n  (c:set-font-size 60)\n  (c:show-text (format nil \"Press SPACE: ~A\" (write-to-string (mod (c:get-ticks) 9))))\n  (setf *calm-redraw* *game-started*))\n```\n\nNote: this variable will be set to `T` whenever a user event was triggered.\n\n##### Variable `*calm-fps*`\n\nThis variable controls how many milliseconds CALM should wait before refreshing the canvas. Setting 0 will use the browser’s `requestAnimationFrame` mechanism to refresh the canvas.\n\nDefault: 42\n\nThis variable only works on the web, for the desktop platform, please check `*calm-delay*`.\n\n#### Drawing on Canvas\n\nDrawing in CALM could be achieved via [Cairo](https://www.cairographics.org/).\n\nTo know more about how to draw anything, please read [Cairo Tutorial](https://www.cairographics.org/tutorial/) and [Cairo API](https://www.cairographics.org/manual/), most of the code could be modified to work in CALM.\n\nFor example:\n\n```c\ncairo_set_line_width (cr, 0.1);\ncairo_set_source_rgb (cr, 0, 0, 0);\ncairo_rectangle (cr, 0.25, 0.25, 0.5, 0.5);\ncairo_stroke (cr);\n```\n\nis equivalent to\n\n```lisp\n(c:set-line-width 0.1)\n(c:set-source-rgb 0 0 0)\n(c:rectangle 0.25 0.25 0.5 0.5)\n(c:stroke)\n```\n\nAll the symbols [exported by cl-cairo2](https://github.com/rpav/cl-cairo2/blob/master/src/package.lisp#L7-L142) should be accessible through `c:` prefix, such as: `c:arc`. On the web, the accessible symbols are limited by [cairo.lisp](https://github.com/VitoVan/calm/blob/main/src/web/cairo.lisp#L266-L405).\n\nSince Cairo is the cardinal drawing facility of CALM, any change of Cairo-related symbols will be considered as a change of CALM itself. Please feel safe to use them.\n\n##### Function `c:rrectangle`\n\nDraw a rounded rectangle.\n\n```lisp\n(defun draw ()\n  (c:set-source-rgb 0 0 1)\n  (c:rrectangle 20 20 100 100 :radius 8) ;; \u003c---- here\n  (c:fill-path))\n```\n\n##### Function `c:show-png`\n\nShow a png file.\n\n```lisp\n(defun draw ()\n  (c:show-png \"assets/calm.png\" 20 20 100 100))\n```\n\nThis function will stretch the png if needed.\n\n#### Rendering Text\n\n##### Function `c:select-font-family`\n\nThis function will select a font to be used in `c:show-text`.\n\n```lisp\n(c:select-font-family \"Open Sans\" :normal :normal)\n```\n\nIt takes three arguments: `family`, `slant` and `weight`. For detailed example, please check `c:show-text`.\n\nTo use a custom font without installing it, just put it inside the **fonts** directory, relative to the file **canvas.lisp**.\n\n##### Function `c:show-text`\n\nThis function will show simple text.\n\n```lisp\n(defun draw ()\n  (c:move-to 30 100)\n  (c:set-font-size 84)\n  (c:select-font-family \"Open Sans\" :italic :bold)\n  (c:show-text \"DON'T PANIC\"))\n```\n\n##### Function `c:show-markup`\n\nThis function will show [Pango Markup](https://docs.gtk.org/Pango/pango_markup.html).\n\n```lisp\n(defun draw ()\n  (c:move-to 20 10)\n  (c:set-font-size 84)\n  (c:show-markup \"This is \u003cspan fgcolor='#245791' weight='bold' face='Open Sans'\u003eSICK\u003c/span\u003e\"))\n```\n\nNote that the coordinate system between `c:show-markup` and `c:show-text` are slightly different, so you may need to adjust the position a little if you switch between `c:show-markup` and `c:show-text`.\n\nThis function is not exposed to the web due to the following reasons:\n\n1. Pango requires multi-threading, which requires extra [HTTP HEADERS](https://web.dev/coop-coep/) to be set\n2. Compiling Pango into WebAssembly will increase the time and data for loading\n3. Using Pango often involves extra fonts to be bundled, this will cause more data to be loaded\n\nSo I don't think this is a good idea to include Pango by default, albeit it is easy to implement.\n\n#### Playing Sound\n\n##### Function `c:play-wav`\n\nPlay a wav file.\n\nIf `c:play-wav` were called before, and the previous wav file was still playing, the sound will be merged together.\n\n```lisp\n(c:play-wav \"assets/ouch.ogg\" :loops 0 :channel -1)\n```\n\nSet `:loops` to -1 means \"infinitely\" (~65000 times)\n\nSet `:channel` to -1 means play on the first free channel\n\nThe maximum number of files playing at the same time is limited to the variable `*calm-audio-numchans*`.\n\n##### Variable `*calm-audio-numchans*`\n\nThe maximum number of wav files being played at the same time.\n\nDefault: 8\n\n##### Function `c:volume-wav`\n\nSet the volume of `c:play-wav`.\n\n```lisp\n(c:volume-wav 128 :channel -1)\n```\n\nThe value should be between 0 (silence) and 128.\n\nSet `:channel` to -1 means all channels.\n\n##### Function `c:halt-wav`\n\nStop playing a channel or all of them.\n\n```lisp\n(c:halt-wav :channel -1)\n```\n\nSet `:channel` to -1 means all channels.\n\n##### Function `c:play-music`\n\nPlay a music file, it can play MP3, Ogg, and WAV.\n\nOther types of files might also work, but they are not guaranteed by CALM.\n\n```lisp\n(c:play-music \"assets/bgm.ogg\" :loops 0)\n```\n\nIf `c:play-music` were called before, and the previous music was still playing, it will be stopped and the latest music will start playing.\n\n##### Function `c:volume-music`\n\nSet the volume of `c:play-music`.\n\n```lisp\n(c:volume-music 128)\n```\n\nThe value should be between 0 (silence) and 128.\n\n##### Function `c:halt-music`\n\nStop playing music.\n\n##### Function `c:play-audio`\n\nPlay an audio file, this function is only available on the web since it utilizes the [HTMLAudioElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement).\n\n```lisp\n(c:play-audio \"assets/meow.ogg\" :loop-audio-p nil :volume 1)\n```\n\n`:volume` should be between 0 and 1.\n\n##### Function `c:halt-audio`\n\nStop playing one specific audio file or all of them.\n\n```lisp\n(c:halt-audio \"assets/purr.ogg\")\n```\n\nIf call it without any arguments, it stops all the playing audio.\n\n\n#### Internal States\n\n##### Variable `*calm-state-mouse-x*`\n\n##### Variable `*calm-state-mouse-y*`\n\n##### Variable `*calm-state-mouse-up*`\n\n##### Variable `*calm-state-mouse-down*`\n\n##### Variable `*calm-state-mouse-just-clicked*`\n\n##### Variable `*calm-state-finger-x*`\n\n##### Variable `*calm-state-finger-y*`\n\n##### Variable `*calm-state-finger-just-tapped*`\n\nThe above variables hold the state of the mouse and finger (touch device, like the mobile web browser), they are read-only. The consequence of `(setf *calm-state-mouse-x* 20)` is equivalent to drinking bleach.\n\n##### Function `c:get-ticks`\n\nThis is just [SDL_GetTicks](https://wiki.libsdl.org/SDL2/SDL_GetTicks).\n\n#### Event Callbacks\n\nThese callbacks are functions that you should define. If you defined any of them, they will be called when the corresponding event was triggered.\n\n##### Callback `on-keydown`\n\n##### Callback `on-keyup`\n\nYou know what these callbacks do, what you don't know is their should-be arguments. Please check `c:keq` for a detailed example.\n\n##### Function `c:keq`\n\nThis function compares the first argument with an infinite number of SDL2 Scancodes, if any of them matched, it will return `T`.\n\n```lisp\n(defun on-keyup (key) ;; keyup handler for evil vimers\n  (cond\n    ((c:keq key :scancode-left :scancode-h)\n     (format t \"move left~%\"))\n    ((c:keq key :scancode-right :scancode-l)\n     (format t \"move right~%\"))\n    ((c:keq key :scancode-up :scancode-k)\n     (format t \"move up~%\"))\n    ((c:keq key :scancode-down :scancode-j)\n     (format t \"move down~%\"))\n    (t (format t \"I don't know what to do~%\"))))\n```\n\nSDL2 Scancode: https://wiki.libsdl.org/SDL2/SDL_Scancode\n\n##### Callback `on-mousewheel`\n\n```lisp\n(defun on-mousewheel (x y direction)\n  ;; your code here\n  )\n```\n\n##### Callback `on-mousemotion`\n\n```lisp\n(defun internal-on-mousemotion (\u0026key x y)\n  ;; your code here\n  )\n```\n\n##### Callback `on-mousebuttonup`\n\n##### Callback `on-mousebuttondown`\n\n```lisp\n(defun on-mousebuttonup (\u0026key button x y clicks)\n  ;; your code here\n  )\n(defun on-mousebuttondown (\u0026key button x y clicks)\n  ;; your code here\n  )\n```\n\n##### Callback `on-fingermotion`\n\n```lisp\n(defun on-fingermotion (\u0026key x  y dx dy pressure finger-id)\n  ;; your code here\n  )\n```\n\n##### Callback `on-fingerup`\n\n##### Callback `on-fingerdown`\n\n```lisp\n(defun on-fingerup (\u0026key x  y dx dy pressure finger-id)\n  ;; your code here\n  )\n(defun on-fingerdown (\u0026key x  y dx dy pressure finger-id)\n  ;; your code here\n  )\n```\n\n##### Callback `on-windowresized`\n\n```lisp\n(defun on-windowresized (width height)\n  ;; your code here\n  )\n```\n\n##### Callback `on-windowenter`\n\n##### Callback `on-windowleave`\n\nThese two callbacks do not take any arguments, for example:\n\n```lisp\n(defun on-windowenter ()\n  ;; your code here\n  )\n```\n\n#### Window Related\n\n##### Variable `*calm-window*`\n\nThis variable is readonly, it holds the created instance of [SDL_Window](https://wiki.libsdl.org/SDL2/SDL_Window).\n\nWith this variable, one could utilize all kinds of SDL2 window related functions, such as:\n\n```lisp\n;; get window position\n(sdl2:get-window-position *calm-window*)\n\n;; set window always on top\n(sdl2-ffi.functions:sdl-set-window-always-on-top\n *calm-window*\n sdl2-ffi:+true+)\n```\n\n##### Variable `*calm-window-x*`\n\n##### Variable `*calm-window-y*`\n\nThe initial position (x, y) of your CALM application window.\n\nDefault: `:centered`\n\n##### Variable `*calm-window-flags*`\n\nA list of [SDL_WindowFlags](https://wiki.libsdl.org/SDL2/SDL_WindowFlags).\n\nYou could set this value like: `(setf *calm-window-flags* '(:shown :allow-highdpi :resizable))`\n\nDefault: `'(:shown :allow-highdpi)`\n\n##### Variable `*calm-window-width*`\n\n##### Variable `*calm-window-height*`\n\n##### Variable `*calm-window-title*`\n\nYou know what these variables do.\n\nIf you don't, [give me 5 bucks](https://github.com/sponsors/VitoVan/) and think harder.\n\n## Tutorials\n\nOk ok, I will write this, I will write this, just wait a second.\n\nYou could check these useful links while waiting:\n\n- SDL2\n    - https://wiki.libsdl.org/SDL2\n    - https://github.com/lispgames/cl-sdl2\n- Cairo\n  - https://www.cairographics.org/\n\n  - https://github.com/rpav/cl-cairo2\n\n- Development Tools\n    - https://slime.common-lisp.dev/\n    - https://lispcookbook.github.io/cl-cookbook/vscode-alive.html\n- Common Lisp\n  - https://lispcookbook.github.io/cl-cookbook/\n  - http://www.lispworks.com/documentation/HyperSpec/Front/\n  - https://lisp-lang.org/books/\n\n## License\n\nThe [source code](https://github.com/VitoVan/calm) is released under GPL-2.0-only.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitovan%2Fcalm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitovan%2Fcalm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitovan%2Fcalm/lists"}