Blender V5.0
space_console.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdio>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_listbase.h"
15#include "BLI_string.h"
16#include "BLI_string_utf8.h"
17
18#include "BKE_context.hh"
19#include "BKE_screen.hh"
20
21#include "ED_screen.hh"
22#include "ED_space_api.hh"
23
24#include "RNA_access.hh"
25#include "RNA_path.hh"
26
27#include "WM_api.hh"
28#include "WM_types.hh"
29
30#include "UI_resources.hh"
31#include "UI_view2d.hh"
32
33#include "BLO_read_write.hh"
34
35#include "console_intern.hh" /* own include */
36
37/* ******************** default callbacks for console space ***************** */
38
39static SpaceLink *console_create(const ScrArea * /*area*/, const Scene * /*scene*/)
40{
41 ARegion *region;
42 SpaceConsole *sconsole;
43
44 sconsole = MEM_callocN<SpaceConsole>("initconsole");
45 sconsole->spacetype = SPACE_CONSOLE;
46
47 sconsole->lheight = 14;
48
49 /* header */
50 region = BKE_area_region_new();
51
52 BLI_addtail(&sconsole->regionbase, region);
55
56 /* main region */
57 region = BKE_area_region_new();
58
59 BLI_addtail(&sconsole->regionbase, region);
61
62 /* keep in sync with info */
64 region->v2d.align |= V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_NEG_Y; /* align bottom left */
65 region->v2d.keepofs |= V2D_LOCKOFS_X;
68 region->v2d.minzoom = region->v2d.maxzoom = 1.0f;
69
70 /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
71 // region->v2d.keepzoom = (V2D_KEEPASPECT|V2D_LIMITZOOM);
72
73 return (SpaceLink *)sconsole;
74}
75
76/* Doesn't free the space-link itself. */
77static void console_free(SpaceLink *sl)
78{
79 SpaceConsole *sc = (SpaceConsole *)sl;
80
81 while (sc->scrollback.first) {
82 console_scrollback_free(sc, static_cast<ConsoleLine *>(sc->scrollback.first));
83 }
84
85 while (sc->history.first) {
86 console_history_free(sc, static_cast<ConsoleLine *>(sc->history.first));
87 }
88}
89
90/* spacetype; init callback */
91static void console_init(wmWindowManager * /*wm*/, ScrArea * /*area*/) {}
92
94{
95 SpaceConsole *sconsolen = static_cast<SpaceConsole *>(MEM_dupallocN(sl));
96
97 /* clear or remove stuff from old */
98
99 /* TODO: duplicate?, then we also need to duplicate the py namespace. */
100 BLI_listbase_clear(&sconsolen->scrollback);
101 BLI_listbase_clear(&sconsolen->history);
102
103 return (SpaceLink *)sconsolen;
104}
105
106/* add handlers, stuff you only do once or on area/region changes */
108{
109 wmKeyMap *keymap;
110 ListBase *lb;
111
112 const float prev_y_min = region->v2d.cur.ymin; /* so re-sizing keeps the cursor visible */
113
114 UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_CUSTOM, region->winx, region->winy);
115
116 /* always keep the bottom part of the view aligned, less annoying */
117 if (prev_y_min != region->v2d.cur.ymin) {
118 const float cur_y_range = BLI_rctf_size_y(&region->v2d.cur);
119 region->v2d.cur.ymin = prev_y_min;
120 region->v2d.cur.ymax = prev_y_min + cur_y_range;
121 }
122
123 /* own keymap */
124 keymap = WM_keymap_ensure(wm->runtime->defaultconf, "Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
125 WM_event_add_keymap_handler_v2d_mask(&region->runtime->handlers, keymap);
126
127 /* Include after "Console" so cursor motion keys such as "Home" isn't overridden. */
128 keymap = WM_keymap_ensure(
129 wm->runtime->defaultconf, "View2D Buttons List", SPACE_EMPTY, RGN_TYPE_WINDOW);
130 WM_event_add_keymap_handler(&region->runtime->handlers, keymap);
131
132 /* add drop boxes */
134
135 WM_event_add_dropbox_handler(&region->runtime->handlers, lb);
136}
137
138/* same as 'text_cursor' */
139static void console_cursor(wmWindow *win, ScrArea * /*area*/, ARegion *region)
140{
141 int wmcursor = WM_CURSOR_TEXT_EDIT;
142 const wmEvent *event = win->eventstate;
143 if (UI_view2d_mouse_in_scrollers(region, &region->v2d, event->xy)) {
144 wmcursor = WM_CURSOR_DEFAULT;
145 }
146
147 WM_cursor_set(win, wmcursor);
148}
149
150/* ************* dropboxes ************* */
151
152static bool console_drop_id_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
153{
154 return WM_drag_get_local_ID(drag, 0) != nullptr;
155}
156
157static void console_drop_id_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
158{
159 ID *id = WM_drag_get_local_ID(drag, 0);
160
161 /* copy drag path to properties */
162 std::string text = RNA_path_full_ID_py(id);
163 RNA_string_set(drop->ptr, "text", text.c_str());
164}
165
166static bool console_drop_path_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
167{
168 return (drag->type == WM_DRAG_PATH);
169}
170
171static void console_drop_path_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
172{
173 char pathname[FILE_MAX + 2];
174 SNPRINTF(pathname, "\"%s\"", WM_drag_get_single_path(drag));
175 RNA_string_set(drop->ptr, "text", pathname);
176}
177
178static bool console_drop_string_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
179{
180 return (drag->type == WM_DRAG_STRING);
181}
182
183static void console_drop_string_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
184{
185 /* NOTE(@ideasman42): Only a single line is supported, multiple lines could be supported
186 * but this implies executing all lines except for the last. While we could consider that,
187 * there are some security implications for this, so just drop one line for now. */
188 std::string str = WM_drag_get_string_firstline(drag);
189 RNA_string_set(drop->ptr, "text", str.c_str());
190}
191
192/* this region dropbox definition */
193static void console_dropboxes()
194{
196
198 lb, "CONSOLE_OT_insert", console_drop_id_poll, console_drop_id_copy, nullptr, nullptr);
200 lb, "CONSOLE_OT_insert", console_drop_path_poll, console_drop_path_copy, nullptr, nullptr);
202 "CONSOLE_OT_insert",
205 nullptr,
206 nullptr);
207}
208
209/* ************* end drop *********** */
210
211static void console_main_region_draw(const bContext *C, ARegion *region)
212{
213 /* draw entirely, view changes should be handled here */
215 View2D *v2d = &region->v2d;
216
219 "CONSOLE_OT_banner",
221 nullptr,
222 nullptr);
223 }
224
225 /* clear and setup matrix */
227
228 /* Works best with no view2d matrix set. */
230
231 /* data... */
232
233 console_history_verify(C); /* make sure we have some command line */
234 console_textview_main(sc, region);
235
236 /* reset view matrix */
238
239 /* scrollers */
240 UI_view2d_scrollers_draw(v2d, nullptr);
241}
242
267
268static void console_keymap(wmKeyConfig *keyconf)
269{
270 WM_keymap_ensure(keyconf, "Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
271}
272
273/****************** header region ******************/
274
275/* add handlers, stuff you only do once or on area/region changes */
277{
278 ED_region_header_init(region);
279}
280
281static void console_header_region_draw(const bContext *C, ARegion *region)
282{
283 ED_region_header(C, region);
284}
285
287{
288 ScrArea *area = params->area;
289 ARegion *region = params->region;
290 const wmNotifier *wmn = params->notifier;
291
292 /* context changes */
293 switch (wmn->category) {
294 case NC_SPACE: {
295 if (wmn->data == ND_SPACE_CONSOLE) {
296 if (wmn->action == NA_EDITED) {
297 if ((wmn->reference && area) && (wmn->reference == area->spacedata.first)) {
298 /* we've modified the geometry (font size), re-calculate rect */
299 console_textview_update_rect(static_cast<SpaceConsole *>(wmn->reference), region);
300 ED_region_tag_redraw(region);
301 }
302 }
303 else {
304 /* generic redraw request */
305 ED_region_tag_redraw(region);
306 }
307 }
308 break;
309 }
310 }
311}
312
314{
315 SpaceConsole *sconsole = (SpaceConsole *)sl;
316
317 BLO_read_struct_list(reader, ConsoleLine, &sconsole->scrollback);
318 BLO_read_struct_list(reader, ConsoleLine, &sconsole->history);
319
320 /* Comma expressions, (e.g. expr1, expr2, expr3) evaluate each expression,
321 * from left to right. the right-most expression sets the result of the comma
322 * expression as a whole. */
323 LISTBASE_FOREACH_MUTABLE (ConsoleLine *, cl, &sconsole->history) {
324 BLO_read_char_array(reader, size_t(cl->len) + 1, &cl->line);
325 if (cl->line) {
326 /* The allocated length is not written, so reset here. */
327 cl->len_alloc = cl->len + 1;
328 }
329 else {
330 BLI_remlink(&sconsole->history, cl);
331 MEM_freeN(cl);
332 }
333 }
334}
335
337{
338 SpaceConsole *con = (SpaceConsole *)sl;
339
340 LISTBASE_FOREACH (ConsoleLine *, cl, &con->history) {
341 /* 'len_alloc' is invalid on write, set from 'len' on read */
342 BLO_write_struct(writer, ConsoleLine, cl);
343 BLO_write_char_array(writer, size_t(cl->len) + 1, cl->line);
344 }
345 BLO_write_struct(writer, SpaceConsole, sl);
346}
347
349{
350 std::unique_ptr<SpaceType> st = std::make_unique<SpaceType>();
351 ARegionType *art;
352
353 st->spaceid = SPACE_CONSOLE;
354 STRNCPY_UTF8(st->name, "Console");
355
356 st->create = console_create;
357 st->free = console_free;
358 st->init = console_init;
359 st->duplicate = console_duplicate;
360 st->operatortypes = console_operatortypes;
361 st->keymap = console_keymap;
362 st->dropboxes = console_dropboxes;
363 st->blend_read_data = console_blend_read_data;
364 st->blend_write = console_space_blend_write;
365
366 /* regions: main window */
367 art = MEM_callocN<ARegionType>("spacetype console region");
370
373 art->cursor = console_cursor;
374 art->event_cursor = true;
376
377 BLI_addhead(&st->regiontypes, art);
378
379 /* regions: header */
380 art = MEM_callocN<ARegionType>("spacetype console region");
382 art->prefsizey = HEADERY;
384
387
388 BLI_addhead(&st->regiontypes, art);
389
390 BKE_spacetype_register(std::move(st));
391}
SpaceConsole * CTX_wm_space_console(const bContext *C)
void BKE_spacetype_register(std::unique_ptr< SpaceType > st)
Definition screen.cc:282
ARegion * BKE_area_region_new()
Definition screen.cc:387
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
#define FILE_MAX
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
#define STRNCPY_UTF8(dst, src)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define BLO_read_struct_list(reader, struct_name, list)
void BLO_read_char_array(BlendDataReader *reader, int64_t array_size, char **ptr_p)
Definition readfile.cc:5770
void BLO_write_char_array(BlendWriter *writer, int64_t num, const char *data_ptr)
#define HEADERY
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_TOP
@ RGN_TYPE_WINDOW
@ RGN_TYPE_HEADER
@ SPACE_CONSOLE
@ SPACE_EMPTY
@ USER_HEADER_BOTTOM
@ V2D_LOCKOFS_X
@ V2D_KEEPTOT_BOUNDS
@ V2D_LIMITZOOM
@ V2D_LOCKZOOM_X
@ V2D_KEEPASPECT
@ V2D_LOCKZOOM_Y
@ V2D_SCROLL_VERTICAL_HIDE
@ V2D_SCROLL_RIGHT
@ V2D_ALIGN_NO_NEG_X
@ V2D_ALIGN_NO_NEG_Y
void ED_region_header(const bContext *C, ARegion *region)
Definition area.cc:3935
void ED_region_header_init(ARegion *region)
Definition area.cc:3950
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
@ ED_KEYMAP_UI
Definition ED_screen.hh:758
@ ED_KEYMAP_HEADER
Definition ED_screen.hh:764
@ ED_KEYMAP_VIEW2D
Definition ED_screen.hh:761
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ TH_BACK
void UI_ThemeClearColor(int colorid)
char char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2]) ATTR_NONNULL(1
void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
Definition view2d.cc:1504
void UI_view2d_view_restore(const bContext *C)
Definition view2d.cc:1162
void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
Definition view2d.cc:221
void UI_view2d_view_ortho(const View2D *v2d)
Definition view2d.cc:1095
@ V2D_COMMONVIEW_CUSTOM
Definition UI_view2d.hh:31
@ WM_DRAG_PATH
Definition WM_types.hh:1208
@ WM_DRAG_STRING
Definition WM_types.hh:1217
#define NA_EDITED
Definition WM_types.hh:584
#define ND_SPACE_CONSOLE
Definition WM_types.hh:519
#define NC_SPACE
Definition WM_types.hh:392
#define U
void console_textview_main(SpaceConsole *sc, const ARegion *region)
void CONSOLE_OT_select_all(wmOperatorType *ot)
void CONSOLE_OT_copy(wmOperatorType *ot)
void CONSOLE_OT_clear(wmOperatorType *ot)
void console_textview_update_rect(SpaceConsole *sc, ARegion *region)
void CONSOLE_OT_select_word(wmOperatorType *ot)
void CONSOLE_OT_delete(wmOperatorType *ot)
void CONSOLE_OT_indent(wmOperatorType *ot)
void CONSOLE_OT_select_set(wmOperatorType *ot)
void CONSOLE_OT_indent_or_autocomplete(wmOperatorType *ot)
void CONSOLE_OT_move(wmOperatorType *ot)
void console_history_free(SpaceConsole *sc, ConsoleLine *cl)
void console_scrollback_free(SpaceConsole *sc, ConsoleLine *cl)
void CONSOLE_OT_clear_line(wmOperatorType *ot)
void CONSOLE_OT_scrollback_append(wmOperatorType *ot)
void CONSOLE_OT_insert(wmOperatorType *ot)
void CONSOLE_OT_history_cycle(wmOperatorType *ot)
void CONSOLE_OT_unindent(wmOperatorType *ot)
void CONSOLE_OT_history_append(wmOperatorType *ot)
void CONSOLE_OT_paste(wmOperatorType *ot)
ConsoleLine * console_history_verify(const bContext *C)
#define str(s)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
std::string RNA_path_full_ID_py(ID *id)
Definition rna_path.cc:1232
static void console_space_blend_write(BlendWriter *writer, SpaceLink *sl)
static void console_operatortypes()
static void console_cursor(wmWindow *win, ScrArea *, ARegion *region)
static bool console_drop_id_poll(bContext *, wmDrag *drag, const wmEvent *)
static bool console_drop_path_poll(bContext *, wmDrag *drag, const wmEvent *)
static void console_dropboxes()
static void console_free(SpaceLink *sl)
static void console_drop_path_copy(bContext *, wmDrag *drag, wmDropBox *drop)
static void console_header_region_draw(const bContext *C, ARegion *region)
static void console_init(wmWindowManager *, ScrArea *)
static void console_main_region_draw(const bContext *C, ARegion *region)
static SpaceLink * console_create(const ScrArea *, const Scene *)
static void console_main_region_listener(const wmRegionListenerParams *params)
static bool console_drop_string_poll(bContext *, wmDrag *drag, const wmEvent *)
void ED_spacetype_console()
static void console_keymap(wmKeyConfig *keyconf)
static void console_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static SpaceLink * console_duplicate(SpaceLink *sl)
static void console_drop_string_copy(bContext *, wmDrag *drag, wmDropBox *drop)
static void console_header_region_init(wmWindowManager *, ARegion *region)
static void console_drop_id_copy(bContext *, wmDrag *drag, wmDropBox *drop)
static void console_main_region_init(wmWindowManager *wm, ARegion *region)
void(* cursor)(wmWindow *win, ScrArea *area, ARegion *region)
void(* listener)(const wmRegionListenerParams *params)
void(* draw)(const bContext *C, ARegion *region)
short event_cursor
void(* init)(wmWindowManager *wm, ARegion *region)
ARegionRuntimeHandle * runtime
Definition DNA_ID.h:414
void * first
ListBase spacedata
ListBase regionbase
ListBase scrollback
float minzoom
short keeptot
short keepzoom
short keepofs
float maxzoom
float ymax
float ymin
eWM_DragDataType type
Definition WM_types.hh:1331
PointerRNA * ptr
Definition WM_types.hh:1420
unsigned int data
Definition WM_types.hh:358
unsigned int action
Definition WM_types.hh:358
unsigned int category
Definition WM_types.hh:358
void * reference
Definition WM_types.hh:360
WindowManagerRuntimeHandle * runtime
struct wmEvent * eventstate
void WM_cursor_set(wmWindow *win, int curs)
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
@ WM_CURSOR_TEXT_EDIT
Definition wm_cursors.hh:16
wmDropBox * WM_dropbox_add(ListBase *lb, const char *idname, bool(*poll)(bContext *C, wmDrag *drag, const wmEvent *event), void(*copy)(bContext *C, wmDrag *drag, wmDropBox *drop), void(*cancel)(Main *bmain, wmDrag *drag, wmDropBox *drop), WMDropboxTooltipFunc tooltip)
const char * WM_drag_get_single_path(const wmDrag *drag)
ListBase * WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
std::string WM_drag_get_string_firstline(const wmDrag *drag)
ID * WM_drag_get_local_ID(const wmDrag *drag, short idcode)
wmEventHandler_Dropbox * WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
wmEventHandler_Keymap * WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
wmEventHandler_Keymap * WM_event_add_keymap_handler_v2d_mask(ListBase *handlers, wmKeyMap *keymap)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:895
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))