├── LICENSE ├── README.md ├── conf.lua ├── loveconsole ├── config.lua └── init.lua └── main.lua /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Catlinman 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # LOVEConsole # 3 | 4 | This is an in-application debugging and command console created for the [LÖVE2D game framework](https://love2d.org/). The console allows for rapid game testing by allowing the execution of user defined commands as well as native Lua functions. It also displays a useful overlay indicating a count of warnings and errors printed to the console to notify the user of any events that occur during execution. 5 | 6 | ## Installation and use ## 7 | 8 | Getting LOVEConsole to run is a fairly simple and straightforward task. All that needs to be done is to put the *loveconsole* folder somewhere in you LÖVE2D project and to then *require* the folder from your *main.lua*. It is suggested that you do it from there since LOVEConsole requires you to hook some of LÖVE's main functions to it. This way it can receive information such as key presses and also draw at the end of the main draw loop. 9 | 10 | An example of how this can be seen in this repository's [*main.lua*](https://github.com/catlinman/loveconsole/blob/master/main.lua) file or somewhat abstracted in the code below. 11 | 12 | ```lua 13 | -- Require the console script and assign it to a variable. 14 | local console = require("loveconsole") 15 | 16 | function love.draw() 17 | -- [Do main drawing operations before drawing the console.] 18 | love.graphics.pop() -- [Do this if you pushed any previous graphic translations.] 19 | console.draw() 20 | end 21 | 22 | function love.keypressed(key) 23 | -- [Handle key presses and then pass these on to the console.] 24 | console.keypressed(key) 25 | end 26 | 27 | function love.textinput(t) 28 | -- [Receive text input and pass it on to the console.] 29 | console.textinput(t) 30 | end 31 | 32 | function love.resize(w, h) 33 | -- [Resize the console if the window is resized.] 34 | console.resize(w, h) 35 | end 36 | ``` 37 | 38 | From there on you can run your application as your normally would. At this point while your application is running you can hit *F10* to bring up the console. This can be done assuming you are using the default key bindings and have not made any changes to the configuration file as explained in the next section. If you've gotten this far your console is pretty much ready to be used. If you want to take it even further you should check out the next section. 39 | 40 | ## Configuring the console ## 41 | 42 | Inside your *loveconsole* folder you will also find a file called *config.lua*. This is where LOVEConsole gets it's configuration from and it is crucial that the configuration file contains all of it's default values as specified in this repositories config file. If this is not the case the console will not be able to run and simply deactivate itself on load. 43 | 44 | You can open the configuration file with your favorite text editing program and modify it to your liking. I've done my best to comment each configuration variable's use so it should be easy to edit and obtain your own personalized console. I also suggest that you define your custom console commands at the end of this file. You will find a sample command at the bottom of the file to see how commands can be created with ease. The benefit of using already defined commands comes with the fact that you have a lot more control over what they do and they are faster to type and perform over a native lua command which can be executed using the *run* built-in console command (further on that in the next section). 45 | 46 | In the configuration file you will also find some variables which define the keys used to perform actions within the console window. You can find their default assignments in the table below. 47 | 48 | 49 | 50 | 53 | 54 | 57 | 58 | 61 | 62 | 65 | 66 | 69 | 70 | 73 | 74 | 77 | 78 | 81 | 82 | 85 | 86 | 89 |
Key 51 | Action 52 |
F10 55 | Toggle the console interface. 56 |
Page Up 59 | Scroll up the console log. 60 |
Page Down 63 | Scroll down the console log. 64 |
Home 67 | Move to the very beginning of the console log. 68 |
End 71 | Move to the newest message in the console log. 72 |
Up Arrow 75 | Move up through the stack of previously entered commands. 76 |
Down Arrow 79 | Move down through the stack of previously entered commands. 80 |
Left Arrow 83 | Move the input cursor to the left. 84 |
Right Arrow 87 | Move the input cursor to the right. 88 |
90 | 91 | ## Built-in commands ## 92 | 93 | LOVEConsole comes with a set of predefined commands which are declared towards the end of the *init.lua* file. Some of these are just to show what the console is capable of while others allow you to interface with native code. You can find a full listing of these commands in the table below. 94 | 95 | 96 | 97 | 100 | 101 | 104 | 105 | 108 | 109 | 112 | 113 | 116 | 117 | 120 | 121 | 124 | 125 | 128 |
Command 98 | Description 99 |
help 102 | Outputs the names and descriptions of all available console commands or just a single one - Arguments: [command to fetch information on] 103 |
clear 106 | Clears the entire console. 107 |
quit 110 | Attempts to close the application. 111 |
print 114 | Prints trailing command arguments as a formatted string - Arguments: [string to print] 115 |
alias 118 | Creates a new command list entry mimicking another command. Arguments: [command to alias] [alias name] 119 |
run 122 | Executes the supplied lua function - Arguments: [lua command to execute] 123 |
set 126 | Sets a supplied variable - Arguments: [lua assignment to execute] 127 |
129 | 130 | ## License ## 131 | 132 | This repository is released under the MIT license. For more information please refer to [LICENSE](https://github.com/catlinman/loveconsole/blob/master/LICENSE) 133 | -------------------------------------------------------------------------------- /conf.lua: -------------------------------------------------------------------------------- 1 | 2 | function love.conf(t) 3 | t.identity = nil 4 | t.version = "0.10.0" 5 | t.console = false 6 | t.accelerometerjoystick = true 7 | t.gammacorrect = false 8 | 9 | t.window.title = "LOVEConsole" 10 | t.window.icon = nil 11 | t.window.width = 800 12 | t.window.height = 600 13 | t.window.borderless = false 14 | t.window.resizable = false 15 | t.window.minwidth = 1 16 | t.window.minheight = 1 17 | t.window.fullscreen = false 18 | t.window.fullscreentype = "desktop" 19 | t.window.vsync = true 20 | t.window.msaa = 0 21 | t.window.display = 1 22 | t.window.highdpi = false 23 | t.window.x = nil 24 | t.window.y = nil 25 | 26 | t.modules.audio = true 27 | t.modules.event = true 28 | t.modules.graphics = true 29 | t.modules.image = true 30 | t.modules.joystick = true 31 | t.modules.keyboard = true 32 | t.modules.math = true 33 | t.modules.mouse = true 34 | t.modules.physics = true 35 | t.modules.sound = true 36 | t.modules.system = true 37 | t.modules.timer = true 38 | t.modules.window = true 39 | t.modules.thread = true 40 | t.modules.video = true 41 | t.modules.touch = true 42 | end 43 | -------------------------------------------------------------------------------- /loveconsole/config.lua: -------------------------------------------------------------------------------- 1 | 2 | -- User configuration file for LOVEConsole 3 | -- We keep this file separate since it mainly consists of defining variables. 4 | 5 | local config = {} -- Table containing the settings returned to the main system. 6 | 7 | config.keys = {} -- Table containing the user defined keys. 8 | config.colors = {} -- Table containing the console style colors. 9 | 10 | config.enabled = true -- If true the user is able to show/hide the console. 11 | config.alert = true -- If true the console will display a widget on warnings and errors. 12 | 13 | config.echoLine = true -- print entered line to console 14 | config.inputChar = ">" -- Characters displayed at the start of the input line. 15 | config.scrollChar = "..." -- Scroll handle characters. 16 | config.cursorSpeed = 1.5 -- Speed at which the cursor blinks. 17 | config.fontName = "" -- Filename of the font to be used. Leave it blank to use the default font. 18 | config.fontSize = 10 -- Size of the console font. 19 | config.consoleMarginEdge = 5 -- Left border margin of the console text. 20 | config.consoleMarginTop = 0 -- Top border margin of the console text. 21 | config.lineSpacing = 4 -- Space between individual lines. 22 | config.outlineSize = 1 -- Outline height at the bottom of the console. 23 | config.ignoreToggleKey = false -- If true, the toggle key will not be inputted to the console's input field. 24 | config.displayPrint = false -- If true the default print function will print to the console. 25 | 26 | config.stackMax = 100 -- Maximum number of lines stored in the console stack before old lines are removed. 27 | config.sizeMin = 5 -- Minimum lines the console should display before extending to the max size. 28 | config.sizeMax = 10 -- Maximum number of entries to print at a time. 29 | config.shiftAmount = 1 -- Amount of lines to move over while scrolling up and down. 30 | 31 | config.keys.toggle = "f10" -- Key used to toggle the console during runtime. 32 | config.keys.scrollUp = "pageup" -- Key used to scroll up within the console's message stack. 33 | config.keys.scrollDown = "pagedown" -- Key used to scroll down within the console's message stack. 34 | config.keys.scrollTop = "home" -- Key used to move to the top of the stack. 35 | config.keys.scrollBottom = "end" -- Key used to move to the bottom of the stack. 36 | config.keys.inputUp = "up" -- Cycle up through the stack of last used commands. 37 | config.keys.inputDown = "down" -- Cycle down through the stack of last used commands. 38 | config.keys.cursorLeft = "left" -- Move the input cursor to the left. 39 | config.keys.cursorRight = "right" -- Move the input cursor to the right. 40 | 41 | -- Color tables used by the console. Change these to style the console to your liking. 42 | -- Background color of the console window. 43 | config.colors["background"] = { 44 | r = 0, 45 | g = 43, 46 | b = 54, 47 | a = 255 48 | } 49 | 50 | -- Color of the console outline. 51 | config.colors["outline"] = { 52 | r = 88, 53 | g = 110, 54 | b = 117, 55 | a = 255 56 | } 57 | 58 | -- Default console basic text color. 59 | config.colors["text"] = { 60 | r = 238, 61 | g = 232, 62 | b = 213, 63 | a = 255 64 | } 65 | 66 | -- Color of warning messages. 67 | config.colors["warning"] = { 68 | r = 231, 69 | g = 207, 70 | b = 0, 71 | a = 255 72 | } 73 | 74 | -- Color of error messages. 75 | config.colors["error"] = { 76 | r = 255, 77 | g = 75, 78 | b = 75, 79 | a = 255 80 | } 81 | 82 | -- Color of error messages. 83 | config.colors["success"] = { 84 | r = 143, 85 | g = 253, 86 | b = 0, 87 | a = 255 88 | } 89 | 90 | -- Color of the console's input field. 91 | config.colors["input"] = { 92 | r = 253, 93 | g = 246, 94 | b = 227, 95 | a = 255 96 | } 97 | 98 | return config 99 | -------------------------------------------------------------------------------- /loveconsole/init.lua: -------------------------------------------------------------------------------- 1 | 2 | --[[ 3 | 4 | LOVEConsole was created by Catlinman and can be forked on GitHub 5 | 6 | -> https://github.com/catlinman/loveconsole 7 | 8 | This file allows for in-application executing and handling of function and variables allowing for more flexibility while debugging. 9 | 10 | Feel free to modify the file to your liking as long as I am credited for the original work. 11 | For more information please refer to the following link: 12 | 13 | -> https://github.com/catlinman/loveconsole/blob/master/LICENSE 14 | 15 | The configuration file (config.lua) contains all the necessary settings for the console to work correctly. 16 | At the moment this means that if a variable within the configuration is not set the actual console will cause errors and not work. 17 | 18 | --]] 19 | 20 | local console = {} -- Base table containing all console variables and methods. 21 | local config = {} -- Table containing console settings. 22 | 23 | local baseFont = love.graphics.getFont() -- Store the default font. 24 | local consoleFont = baseFont -- Temporarily store the default font which will be overwritten later on. 25 | 26 | local consoleCommands = {} -- Table containing command callbacks. 27 | local consoleInput = "" -- String containing a user entered string command. 28 | local consoleActive = false -- If true the console will be shown. 29 | local consoleStatus = "" -- Variable holding the last fatal error message. 30 | local consoleCursorIndex = 0 -- Index of the input movement cursor. 31 | 32 | local consoleStack = {} -- Table containing the console printed lines. 33 | local consoleStackCount = 0 -- Current number of lines the console should accommodate for. 34 | local consoleStackShift = 0 -- Amount of lines to shift the output stack by. 35 | 36 | local consoleInputStack = {} -- Table containing the last user inputs. Has the same size as the main stack. 37 | local consoleInputStackCount = 0 -- Number of input lines in the stack. 38 | local consoleInputStackShift = 0 -- Amount of lines to shift the input stack by. 39 | 40 | local warningCount, errorCount = 0, 0 -- Track the number of unchecked errors and warnings. 41 | 42 | local screenWidth, screenHeight = love.graphics.getDimensions() -- Store the screen size. 43 | 44 | local defaultPrint = print 45 | 46 | -- Returns the current lua local path to this script. 47 | local function scriptPath() 48 | local str = debug.getinfo(2, "S").source:sub(2) 49 | return str:match("(.*/)") or "" 50 | end 51 | 52 | -- String splitting function. 53 | function string.split(str, delim) 54 | if string.find(str, delim) == nil then 55 | return {str} 56 | end 57 | 58 | local result = {} 59 | local pat = "(.-)" .. delim .. "()" 60 | local nb = 0 61 | local lastPos 62 | 63 | for part, pos in string.gfind(str, pat) do 64 | nb = nb + 1 65 | result[nb] = part 66 | lastPos = pos 67 | end 68 | 69 | result[nb + 1] = string.sub(str, lastPos) 70 | 71 | return result 72 | end 73 | 74 | -- Insert a string into another string. 75 | function string.insert(s1, s2, pos) 76 | return string.sub(s1, 1, pos) ..s2 ..string.sub(s1, pos + 1, #s1) 77 | end 78 | 79 | -- Drop a character at a specified position. 80 | function string.pop(str, pos) 81 | return string.sub(str, 1, pos) ..string.sub(str, pos + 2, #str) 82 | end 83 | 84 | -- Remove all UTF8 characters from a string. 85 | function string.stripUTF8(str) 86 | return str:gsub('[%z\1-\127\194-\244][\128-\191]*', function(c) return #c > 1 and "" end) 87 | end 88 | 89 | -- Range iterator function. 90 | local function range(a, b, step) 91 | if not b then 92 | b = a 93 | a = 1 94 | end 95 | 96 | step = step or 1 97 | 98 | local f = step > 0 and 99 | function(_, lastvalue) 100 | local nextvalue = lastvalue + step 101 | if nextvalue <= b then return nextvalue end 102 | end or 103 | step < 0 and 104 | function(_, lastvalue) 105 | local nextvalue = lastvalue + step 106 | if nextvalue >= b then return nextvalue end 107 | end or 108 | function(_, lastvalue) return lastvalue end 109 | return f, nil, a - step 110 | end 111 | 112 | -- Pass a message to the console stack. 113 | local function stackpush(message, color) 114 | if message ~= nil then 115 | if #consoleStack > config.stackMax then 116 | table.remove(consoleStack, 1) 117 | end 118 | 119 | consoleStack[#consoleStack + 1] = { 120 | ["message"] = message, 121 | ["color"] = color 122 | } 123 | 124 | consoleStackCount = #consoleStack 125 | end 126 | end 127 | 128 | -- Inserts line breaks into a string. 129 | local function linify(message) 130 | local editedmessage = message 131 | local averagelength = consoleFont:getWidth(message) / #tostring(message) 132 | local segementlength = math.floor((screenWidth - config.consoleMarginEdge * 2) / averagelength) 133 | local numsplits = math.floor(#tostring(message) / segementlength) 134 | 135 | if numsplits > 0 then 136 | for i = 1, numsplits, 1 do 137 | editedmessage = string.insert(editedmessage, '\n', segementlength * i + (2 * (i - 1))) 138 | end 139 | end 140 | 141 | return string.split(tostring(editedmessage), '\n') 142 | end 143 | 144 | -- Console functions that can be called outside of console.lua 145 | -- Toggle the console. 146 | function console.toggle(state) 147 | if config.enabled then 148 | if state ~= true and state ~= false then 149 | consoleActive = not consoleActive 150 | 151 | elseif state or state == false then 152 | -- The console state was specified. Set the console to the desired state. 153 | consoleActive = state 154 | end 155 | 156 | if consoleActive then 157 | -- The console was opened and the errors were seen. Remove the outside widget. 158 | warningCount, errorCount = 0, 0 159 | end 160 | end 161 | end 162 | 163 | -- Print a string to the console. The optional color table must be defined as {r = v, g = v, b = v, a = v}. 164 | function console.print(message, color) 165 | if config.enabled then 166 | if message ~= nil then 167 | 168 | lines = linify(message) 169 | 170 | for i, m in pairs(lines) do 171 | if not color then 172 | stackpush(m, "default") 173 | else 174 | stackpush(m, color) 175 | end 176 | end 177 | else 178 | stackpush("Please supply a value before printing to the console.", "warning") 179 | end 180 | end 181 | end 182 | 183 | -- Print a string to the console with warning styling and add to the warning count. 184 | function console.warning(message) 185 | if config.enabled then 186 | if message ~= nil then 187 | lines = linify(message) 188 | 189 | for i, m in pairs(lines) do 190 | stackpush("Warning: " .. m, "warning") 191 | end 192 | 193 | warningCount = warningCount + 1 194 | else 195 | stackpush("Please supply a value before sending a warning message to the console.", "warning") 196 | end 197 | end 198 | end 199 | 200 | -- Print a string to the console with success styling. 201 | function console.success(message) 202 | if config.enabled then 203 | if message ~= nil then 204 | lines = linify(message) 205 | 206 | for i, m in pairs(lines) do 207 | stackpush("Success: " .. m, "success") 208 | end 209 | else 210 | stackpush("Please supply a value before sending a warning message to the console.", "warning") 211 | end 212 | end 213 | end 214 | 215 | -- Print a string to the console with error styling and add to the error count. 216 | function console.error(message) 217 | if config.enabled then 218 | if message ~= nil then 219 | lines = linify(message) 220 | 221 | for i, m in pairs(lines) do 222 | stackpush("Error: " .. m, "error") 223 | end 224 | 225 | errorCount = errorCount + 1 226 | else 227 | stackpush("Please supply a value before sending an error message to the console.", "warning") 228 | end 229 | end 230 | end 231 | 232 | -- Print a string to the console using the default print function. 233 | function print(...) 234 | defaultPrint(...) 235 | 236 | if config.displayPrint then 237 | console.print(table.concat({...}, " ")) 238 | end 239 | end 240 | 241 | -- Add a command to the command table. Callback is the function executed when the command is run. 242 | function console.addCommand(name, callback, description) 243 | if not consoleCommands[name] then 244 | consoleCommands[name] = {["callback"] = callback, ["description"] = description or ""} 245 | else 246 | print("[Console] The command with the name of " .. name .. " already exists in the command table.") 247 | end 248 | end 249 | 250 | -- Remove a command from the command table. 251 | function console.removeCommand(name) 252 | if consoleCommands[name] then 253 | consoleCommands[name] = nil 254 | collectgarbage() 255 | else 256 | print("[Console] Unable to find the command with the name of " .. name .. " in the command table.") 257 | end 258 | end 259 | 260 | -- Tell the console to perform a command from a string argument. 261 | function console.perform(line) 262 | local arguments = string.split(line, " ") 263 | local command = arguments[1] 264 | 265 | -- Remove the command argument from the argument table 266 | table.remove(arguments, 1) 267 | 268 | if consoleCommands[command] then 269 | local status, err 270 | 271 | if arguments[1] then 272 | status, err = pcall( 273 | function() 274 | consoleCommands[command].callback(arguments) 275 | end 276 | ) 277 | else 278 | status, err = pcall( 279 | function() 280 | consoleCommands[command].callback() 281 | end 282 | ) 283 | end 284 | 285 | if err then 286 | console.print(string.format("Executing %s returned the following error: %s", command, tostring(err))) 287 | end 288 | elseif console.parseLine then 289 | console.parseLine(line) 290 | else 291 | console.print(string.format("Unknown command '%s'", command)) 292 | end 293 | end 294 | 295 | -- Clear the console. 296 | function console.clear() 297 | consoleStack = {} 298 | consoleStackCount = 0 299 | warningCount, errorCount = 0, 0 300 | end 301 | 302 | -- These functions need to be called from main.lua 303 | -- Draw the console and it's contents. 304 | function console.draw() 305 | if config.enabled and consoleActive then 306 | love.graphics.setFont(consoleFont) -- Prepare the console font. 307 | 308 | -- Draw the console background. 309 | love.graphics.setColor(config.colors["background"].r, config.colors["background"].g, config.colors["background"].b, config.colors["background"].a) 310 | love.graphics.rectangle( 311 | "fill", 312 | 0, 313 | 0, 314 | screenWidth, 315 | config.consoleMarginTop + config.fontSize * 2 + config.lineSpacing * math.max(math.min(consoleStackCount, config.sizeMax) + 1, config.sizeMin) + 316 | (math.max(math.min(consoleStackCount, config.sizeMax), config.sizeMin - 1) * config.fontSize) 317 | ) 318 | 319 | -- Draw the console outline. 320 | love.graphics.setColor(config.colors["outline"].r, config.colors["outline"].g, config.colors["outline"].b, config.colors["outline"].a) 321 | love.graphics.rectangle( 322 | "fill", 323 | 0, 324 | config.consoleMarginTop + config.fontSize * 2 + config.lineSpacing * math.max(math.min(consoleStackCount, config.sizeMax) + 1, config.sizeMin) + 325 | (math.max(math.min(consoleStackCount, config.sizeMax), config.sizeMin - 1) * config.fontSize), 326 | screenWidth, 327 | config.outlineSize 328 | ) 329 | 330 | -- Draw the scroll indicators. 331 | if #consoleStack > config.sizeMax then 332 | love.graphics.setColor(config.colors["text"].r, config.colors["text"].g, config.colors["text"].b, config.colors["text"].a) 333 | 334 | -- Show scroll arrows if there are more lines to display. 335 | if consoleStackShift ~= math.min(#consoleStack - config.sizeMax, config.stackMax) then 336 | love.graphics.printf(config.scrollChar, 0, config.consoleMarginTop, screenWidth - config.consoleMarginEdge, "right") 337 | end 338 | 339 | if consoleStackShift ~= 0 then 340 | love.graphics.printf(config.scrollChar, 0, config.consoleMarginTop + (config.lineSpacing * config.sizeMax) + (config.sizeMax * config.fontSize), screenWidth - config.consoleMarginEdge, "right") 341 | end 342 | end 343 | 344 | -- Draw the message stack with the message coloring. 345 | for i in range(math.min(config.sizeMax, #consoleStack)) do 346 | local entry = consoleStack[math.max(1, (#consoleStack - math.min(config.sizeMax, #consoleStack) + i - consoleStackShift))] 347 | 348 | if type(entry.color) == "string" then 349 | if config.colors[entry.color] then 350 | local c = config.colors[entry.color] 351 | love.graphics.setColor(c.r, c.g, c.b, c.a) 352 | else 353 | love.graphics.setColor(config.colors["text"].r, config.colors["text"].g, config.colors["text"].b, config.colors["text"].a) 354 | end 355 | 356 | elseif type(entry.color) == "table" then 357 | local r, g, b, a = entry.color.r or 255, entry.color.g or 255, entry.color.b or 255, entry.color.a or 255 358 | love.graphics.setColor(r, g, b, a) 359 | else 360 | love.graphics.setColor(config.colors["text"].r, config.colors["text"].g, config.colors["text"].b, config.colors["text"].a) 361 | end 362 | 363 | love.graphics.print(tostring(entry.message), config.consoleMarginEdge, config.consoleMarginTop + (config.lineSpacing * i) + ((i - 1) * config.fontSize)) 364 | end 365 | 366 | -- Draw the input line. 367 | local consoleInputEdited = consoleInput 368 | if math.ceil(os.clock() * config.cursorSpeed) % 2 == 0 then 369 | consoleInputEdited = string.insert(consoleInput, "|", consoleCursorIndex) 370 | else 371 | consoleInputEdited = string.insert(consoleInput, " ", consoleCursorIndex) 372 | end 373 | 374 | love.graphics.setColor(config.colors["input"].r, config.colors["input"].g, config.colors["input"].b, config.colors["input"].a) 375 | love.graphics.print(string.format("%s%s", config.inputChar, consoleInputEdited), config.consoleMarginEdge,config.consoleMarginTop + 376 | (config.lineSpacing * math.max(math.min(consoleStackCount, config.sizeMax) + 1, 1)) + 377 | (math.min(consoleStackCount, config.sizeMax) * config.fontSize) 378 | ) 379 | 380 | -- Reset the color and font in case someone decides to do drawing after the console (which doesn't make sense but who cares). 381 | love.graphics.setColor(255, 255, 255, 255) 382 | love.graphics.setFont(baseFont) 383 | 384 | elseif config.enabled and config.alert and consoleActive == false then 385 | love.graphics.setFont(consoleFont) -- Prepare the console font. 386 | 387 | -- Draw the information widgets if the console is hidden and there are warnings and or errors. 388 | if warningCount > 0 or errorCount > 0 then 389 | local width = 6 * config.fontSize 390 | local height = config.fontSize * 1.5 391 | 392 | -- Draw the box outline border. 393 | love.graphics.setColor(config.colors["outline"].r, config.colors["outline"].g, config.colors["outline"].b, config.colors["outline"].a) 394 | love.graphics.rectangle("fill", 0, 0, width + config.outlineSize * 2, height + config.outlineSize * 2) 395 | 396 | -- Draw the box background. 397 | love.graphics.setColor(config.colors["background"].r, config.colors["background"].g, config.colors["background"].b, config.colors["background"].a) 398 | love.graphics.rectangle("fill", config.outlineSize, config.outlineSize, width, height) 399 | 400 | -- Draw the warning count. 401 | love.graphics.setColor(config.colors["warning"].r, config.colors["warning"].g, config.colors["warning"].b, config.colors["warning"].a) 402 | love.graphics.printf(math.min(9999, warningCount), math.ceil(config.outlineSize - config.fontSize / 2), math.ceil(config.outlineSize + (config.fontSize / 6)), config.fontSize * 4, "center") 403 | 404 | -- Draw the error count. 405 | love.graphics.setColor(config.colors["error"].r, config.colors["error"].g, config.colors["error"].b, config.colors["error"].a) 406 | love.graphics.printf(math.min(9999, errorCount), math.ceil(config.outlineSize + config.fontSize * 2.5), math.ceil(config.outlineSize + (config.fontSize / 6)), config.fontSize * 4, "center") 407 | 408 | -- Reset color. 409 | love.graphics.setColor(255, 255, 255, 255) 410 | love.graphics.setFont(baseFont) 411 | end 412 | end 413 | end 414 | 415 | -- Tell LÖVE2D to allow repeating key presses. Comment this line if you don't want the given functionality. 416 | love.keyboard.setKeyRepeat(true) 417 | 418 | -- Receive pressed keys and interpret them. 419 | function console.keypressed(key) 420 | if config.enabled then 421 | if key == config.keys.toggle then 422 | -- Update the screen size and display the console. 423 | screenWidth, screenHeight = love.graphics.getDimensions() 424 | console.toggle() 425 | 426 | elseif consoleActive then 427 | if key == "return" or key == "kpenter" then 428 | if consoleInput == "" then 429 | consoleInput = lastConsoleInput or "" 430 | end 431 | 432 | if consoleInput ~= "" then 433 | lastConsoleInput = consoleInput 434 | if consoleInput:match("%S") then 435 | -- Store the line in the stack. 436 | if #consoleInputStack > config.stackMax then 437 | table.remove(consoleInputStack, 1) 438 | end 439 | 440 | consoleInputStack[#consoleInputStack + 1] = consoleInput 441 | consoleInputStackCount = #consoleInputStack 442 | 443 | -- echo line if enabled 444 | if config.echoLine then 445 | console.print(string.format("%s%s", config.inputChar, consoleInput)) 446 | end 447 | 448 | -- Execute the given string command and reset the input field. 449 | console.perform(consoleInput) 450 | consoleInput = "" 451 | 452 | -- Also reset the stack shift. 453 | consoleStackShift = 0 454 | consoleInputStackShift = 0 455 | 456 | -- Reset the cursor index 457 | consoleCursorIndex = 0 458 | else 459 | consoleInput = "" 460 | end 461 | end 462 | 463 | elseif key == "backspace" then 464 | if consoleCursorIndex ~= 0 then 465 | consoleInput = string.pop(consoleInput, consoleCursorIndex - 1) 466 | consoleCursorIndex = math.max(consoleCursorIndex - 1, 0) 467 | end 468 | 469 | elseif key == "delete" then 470 | consoleInput = string.pop(consoleInput, consoleCursorIndex) 471 | 472 | elseif love.keyboard.isDown("lctrl", "rctrl") and love.keyboard.isDown("v") then 473 | consoleInput = string.insert(consoleInput, love.system.getClipboardText(), consoleCursorIndex) 474 | consoleCursorIndex = consoleCursorIndex + string.len(love.system.getClipboardText()) 475 | 476 | elseif key == config.keys.scrollUp then 477 | if #consoleStack > config.sizeMax then 478 | -- Move the stack up. 479 | consoleStackShift = math.min(math.min(#consoleStack - config.sizeMax, config.stackMax), consoleStackShift + 1) 480 | end 481 | 482 | elseif key == config.keys.scrollDown then 483 | -- Move the stack down. 484 | consoleStackShift = math.max(consoleStackShift - 1, 0) 485 | 486 | elseif key == config.keys.scrollTop then 487 | -- Make sure that we can actually scroll and if so, move the stack shift to show the top most line. 488 | if #consoleStack > config.sizeMax then 489 | consoleStackShift = math.min(#consoleStack - config.sizeMax, config.stackMax) 490 | end 491 | 492 | elseif key == config.keys.scrollBottom then 493 | -- Set the shift amount to zero so the newest line is the last. 494 | consoleStackShift = 0 495 | 496 | elseif key == config.keys.scrollUp then 497 | if #consoleStack > config.sizeMax then 498 | -- Move the stack up. 499 | consoleStackShift = math.min(math.min(#consoleStack - config.sizeMax, config.stackMax), consoleStackShift + 1) 500 | end 501 | 502 | elseif key == config.keys.cursorLeft then 503 | consoleCursorIndex = math.max(consoleCursorIndex - 1, 0) 504 | 505 | elseif key == config.keys.cursorRight then 506 | consoleCursorIndex = math.min(consoleCursorIndex + 1, #consoleInput) 507 | 508 | elseif key == config.keys.inputUp then 509 | consoleInputStackShift = math.min(consoleInputStackShift + 1, #consoleInputStack) 510 | 511 | local entry = consoleInputStack[#consoleInputStack - consoleInputStackShift + 1] 512 | if entry then 513 | consoleInput = entry 514 | end 515 | 516 | consoleCursorIndex = #consoleInput 517 | 518 | elseif key == config.keys.inputDown then 519 | consoleInputStackShift = math.max(consoleInputStackShift - 1, 0) 520 | 521 | local entry = consoleInputStack[#consoleInputStack - consoleInputStackShift + 1] 522 | if consoleInputStackShift ~= 0 then 523 | consoleInput = entry 524 | consoleCursorIndex = #consoleInput 525 | else 526 | consoleInput = "" 527 | consoleCursorIndex = 0 528 | end 529 | end 530 | end 531 | end 532 | end 533 | 534 | -- Send text input to the console input field. 535 | function console.textinput(s) 536 | -- If the key is the toggle key and the ignoreToggleKey option is enabled, clear the input. 537 | if config.ignoreToggleKey and s == config.keys.toggle then 538 | s = "" 539 | end 540 | 541 | if config.enabled and consoleActive and s ~= "" then 542 | -- Insert the character and clean out all UTF8 characters since they break everything otherwise. 543 | consoleInput = string.insert(consoleInput, string.stripUTF8(s), consoleCursorIndex) 544 | consoleCursorIndex = math.min(#consoleInput, consoleCursorIndex + 1) 545 | end 546 | end 547 | 548 | -- If the window is resized whilst the console is open, resize the console. 549 | function console.resize(w, h) 550 | screenWidth, screenHeight = w, h 551 | end 552 | 553 | -- Execute the configuration file and initialize user consoleCommands. 554 | local loaded, data 555 | loaded, data = pcall(love.filesystem.load, scriptPath() .."config.lua") 556 | 557 | if not loaded then 558 | print("[Console] Failed to load the configuration file due to the following error: " .. tostring(data)) 559 | config.enabled, consoleActive, consoleStatus = false, false, data 560 | else 561 | loaded, data = pcall(data) 562 | 563 | if not loaded then 564 | print("[Console] Executing the configuration file returned the following error: " .. tostring(data)) 565 | config.enabled, consoleActive, consoleStatus = false, false, data 566 | else 567 | config = data 568 | 569 | -- Load font data. 570 | local fontstatus, data = pcall( 571 | function() 572 | if config.fontName ~= "" then 573 | return love.graphics.newFont(config.fontName, config.fontSize) 574 | else 575 | return love.graphics.newFont(config.fontSize) 576 | end 577 | end 578 | ) 579 | if not fontstatus then 580 | print("[Console] Loading the custom defined console font returned the following error: " .. tostring(data) .." - reverting to the default font instead.") 581 | else 582 | consoleFont = data 583 | end 584 | end 585 | end 586 | 587 | -- Some base functions to make life just a little easier. 588 | 589 | -- We wrap functions in a custom callback. 590 | console.addCommand("clear", function() console.clear() end, "Clears the entire console.") 591 | console.addCommand("quit", function() love.event.quit() end, "Attempts to close the application.") 592 | 593 | -- Command callbacks can also receive a table of string arguments. 594 | console.addCommand("print", function(args) 595 | if args then 596 | console.print(table.concat(args, " ")) 597 | else 598 | -- Error is returned to the console. In case of console.execute, error is returned to the "out" variable. 599 | console.print("Missing required arguments") 600 | end 601 | end, "Prints trailing command arguments as a formatted string - Arguments: [string to print]") 602 | 603 | -- Executes a lua command and prints it's return value to the console. 604 | console.addCommand("run", function(args) 605 | if args then 606 | local value = assert(loadstring(string.format("return %s", table.concat(args, " "))))() 607 | 608 | if value then 609 | console.print(string.format("Returned %s", tostring(value))) 610 | else 611 | console.print(string.format("Executing %s returned nil", table.concat(args, " "))) 612 | end 613 | else 614 | console.print("Missing the argument lua code to execute") 615 | end 616 | end, "Executes the supplied lua function - Arguments: [lua command to execute] - Example: 'console.print(\"Do the fishstick!\")'") 617 | 618 | -- Same as run with the difference of not returning a value and so avoiding errors while assigning new values to variables. 619 | console.addCommand("set", function(args) 620 | if args then 621 | assert(loadstring(string.format('%s', table.concat(args, " "))))() 622 | console.print("Variable entry set") 623 | else 624 | console.print("Missing the argument lua code to set") 625 | end 626 | end, "Sets a supplied variable - Arguments: [lua assignment to execute] - Example: 'console.enabled = false'") 627 | 628 | -- Amazing help command of doom. It helps people. 629 | console.addCommand("help", function(args) 630 | if not args then 631 | console.print("Available commands are:") 632 | for k, v in pairs(consoleCommands) do 633 | if v.description ~= "" then 634 | console.print(string.format("%s - %s", k, v.description), config.colors.success) 635 | else 636 | console.print(k, config.colors.success) 637 | end 638 | end 639 | else 640 | local name = table.concat(args, " ") 641 | 642 | if consoleCommands[name] then 643 | if consoleCommands[name].description then 644 | console.print(string.format("%s - %s", name, consoleCommands[name].description), {r = 0, g = 255, b = 0}) 645 | else 646 | console.print(string.format("The command with the name of '%s' does not have a description.", name)) 647 | end 648 | else 649 | console.print(string.format("The command with the name of '%s' was not found in the command table.", name)) 650 | end 651 | end 652 | end, "Outputs the names and descriptions of all available console commands or just a single one - Arguments: [command to fetch information on]") 653 | 654 | -- Creates a new command entry that points to another command. 655 | console.addCommand("alias", function(args) 656 | if args then 657 | if args[1] and args[2] then 658 | if consoleCommands[args[1]] then 659 | console.addCommand(args[2], consoleCommands[args[1]].callback, consoleCommands[args[1]].description) 660 | console.print(string.format("Successfully assigned the alias of '%s' to the command of '%s'.", args[2], args[1])) 661 | end 662 | else 663 | console.print("Missing command arguments. Requires two.") 664 | end 665 | else 666 | console.print("Missing command arguments. Requires two.") 667 | end 668 | end, "Creates a new command list entry mimicking another command. Arguments: [command to alias] [alias name]") 669 | 670 | return console 671 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | 2 | -- Example main file to show the console functionality. 3 | 4 | local console = require("loveconsole") -- Require the console script. 5 | 6 | function love.load() 7 | -- Example of printing to the console with custom colors. 8 | console.print("You are running LOVEConsole by Catlinman", {r = 0, g = 150, b = 255, a = 255}) 9 | console.warning("Wow what a warning. Something is about to break.") 10 | console.error("Many error very scare.") 11 | console.success("User is spooked.") 12 | 13 | -- We define custom commands using the addCommand method. 14 | -- A simple "Hello user" example command can be seen below. 15 | console.addCommand("hello", function(args) 16 | if args then 17 | console.print(string.format("Greetings %s!", args[1])) 18 | else 19 | console.print("Hey there!") 20 | end 21 | end, "Greets you in a non rude way - Arguments: [person to say hello to]") 22 | 23 | end 24 | 25 | function love.draw() 26 | love.graphics.setBackgroundColor(10, 25, 50) -- Set a lame background. 27 | 28 | -- Keep in mind to call love.graphics.pop() if you performed any translation changes using love.graphics.push() 29 | console.draw() -- Draw the console at the end of the draw loop. 30 | end 31 | 32 | function love.keypressed(key) 33 | console.keypressed(key) -- Pass pressed keys to the console. 34 | end 35 | 36 | function love.textinput(t) 37 | console.textinput(t) -- Send text input to the console. 38 | end 39 | 40 | function love.resize(w, h) 41 | console.resize(w, h) -- Resize the console if the window is resized. 42 | end 43 | --------------------------------------------------------------------------------