Blender V4.3
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
9#include <cstdio>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_blenlib.h"
15#include "BLI_utildefines.h"
16
17#include "BKE_context.hh"
18#include "BKE_screen.hh"
19
20#include "ED_screen.hh"
21#include "ED_space_api.hh"
22
23#include "RNA_access.hh"
24#include "RNA_path.hh"
25
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "UI_resources.hh"
30#include "UI_view2d.hh"
31
32#include "BLO_read_write.hh"
33
34#include "console_intern.hh" /* own include */
35
36/* ******************** default callbacks for console space ***************** */
37
38static SpaceLink *console_create(const ScrArea * /*area*/, const Scene * /*scene*/)
39{
40 ARegion *region;
41 SpaceConsole *sconsole;
42
43 sconsole = static_cast<SpaceConsole *>(MEM_callocN(sizeof(SpaceConsole), "initconsole"));
44 sconsole->spacetype = SPACE_CONSOLE;
45
46 sconsole->lheight = 14;
47
48 /* header */
49 region = static_cast<ARegion *>(MEM_callocN(sizeof(ARegion), "header for console"));
50
51 BLI_addtail(&sconsole->regionbase, region);
52 region->regiontype = RGN_TYPE_HEADER;
53 region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
54
55 /* main region */
56 region = static_cast<ARegion *>(MEM_callocN(sizeof(ARegion), "main region for text"));
57
58 BLI_addtail(&sconsole->regionbase, region);
59 region->regiontype = RGN_TYPE_WINDOW;
60
61 /* keep in sync with info */
62 region->v2d.scroll |= V2D_SCROLL_RIGHT;
63 region->v2d.align |= V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_NEG_Y; /* align bottom left */
64 region->v2d.keepofs |= V2D_LOCKOFS_X;
65 region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
66 region->v2d.keeptot = V2D_KEEPTOT_BOUNDS;
67 region->v2d.minzoom = region->v2d.maxzoom = 1.0f;
68
69 /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
70 // region->v2d.keepzoom = (V2D_KEEPASPECT|V2D_LIMITZOOM);
71
72 return (SpaceLink *)sconsole;
73}
74
75/* Doesn't free the space-link itself. */
76static void console_free(SpaceLink *sl)
77{
78 SpaceConsole *sc = (SpaceConsole *)sl;
79
80 while (sc->scrollback.first) {
81 console_scrollback_free(sc, static_cast<ConsoleLine *>(sc->scrollback.first));
82 }
83
84 while (sc->history.first) {
85 console_history_free(sc, static_cast<ConsoleLine *>(sc->history.first));
86 }
87}
88
89/* spacetype; init callback */
90static void console_init(wmWindowManager * /*wm*/, ScrArea * /*area*/) {}
91
93{
94 SpaceConsole *sconsolen = static_cast<SpaceConsole *>(MEM_dupallocN(sl));
95
96 /* clear or remove stuff from old */
97
98 /* TODO: duplicate?, then we also need to duplicate the py namespace. */
99 BLI_listbase_clear(&sconsolen->scrollback);
100 BLI_listbase_clear(&sconsolen->history);
101
102 return (SpaceLink *)sconsolen;
103}
104
105/* add handlers, stuff you only do once or on area/region changes */
107{
108 wmKeyMap *keymap;
109 ListBase *lb;
110
111 const float prev_y_min = region->v2d.cur.ymin; /* so re-sizing keeps the cursor visible */
112
113 /* force it on init, for old files, until it becomes config */
114 region->v2d.scroll = (V2D_SCROLL_RIGHT);
115
116 UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_CUSTOM, region->winx, region->winy);
117
118 /* always keep the bottom part of the view aligned, less annoying */
119 if (prev_y_min != region->v2d.cur.ymin) {
120 const float cur_y_range = BLI_rctf_size_y(&region->v2d.cur);
121 region->v2d.cur.ymin = prev_y_min;
122 region->v2d.cur.ymax = prev_y_min + cur_y_range;
123 }
124
125 /* own keymap */
127 WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
128
129 /* Include after "Console" so cursor motion keys such as "Home" isn't overridden. */
130 keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", SPACE_EMPTY, RGN_TYPE_WINDOW);
131 WM_event_add_keymap_handler(&region->handlers, keymap);
132
133 /* add drop boxes */
135
136 WM_event_add_dropbox_handler(&region->handlers, lb);
137}
138
139/* same as 'text_cursor' */
140static void console_cursor(wmWindow *win, ScrArea * /*area*/, ARegion *region)
141{
142 int wmcursor = WM_CURSOR_TEXT_EDIT;
143 const wmEvent *event = win->eventstate;
144 if (UI_view2d_mouse_in_scrollers(region, &region->v2d, event->xy)) {
145 wmcursor = WM_CURSOR_DEFAULT;
146 }
147
148 WM_cursor_set(win, wmcursor);
149}
150
151/* ************* dropboxes ************* */
152
153static bool console_drop_id_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
154{
155 return WM_drag_get_local_ID(drag, 0) != nullptr;
156}
157
158static void console_drop_id_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
159{
160 ID *id = WM_drag_get_local_ID(drag, 0);
161
162 /* copy drag path to properties */
163 std::string text = RNA_path_full_ID_py(id);
164 RNA_string_set(drop->ptr, "text", text.c_str());
165}
166
167static bool console_drop_path_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
168{
169 return (drag->type == WM_DRAG_PATH);
170}
171
172static void console_drop_path_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
173{
174 char pathname[FILE_MAX + 2];
175 SNPRINTF(pathname, "\"%s\"", WM_drag_get_single_path(drag));
176 RNA_string_set(drop->ptr, "text", pathname);
177}
178
179static bool console_drop_string_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
180{
181 return (drag->type == WM_DRAG_STRING);
182}
183
184static void console_drop_string_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
185{
186 /* NOTE(@ideasman42): Only a single line is supported, multiple lines could be supported
187 * but this implies executing all lines except for the last. While we could consider that,
188 * there are some security implications for this, so just drop one line for now. */
189 std::string str = WM_drag_get_string_firstline(drag);
190 RNA_string_set(drop->ptr, "text", str.c_str());
191}
192
193/* this region dropbox definition */
194static void console_dropboxes()
195{
197
199 lb, "CONSOLE_OT_insert", console_drop_id_poll, console_drop_id_copy, nullptr, nullptr);
201 lb, "CONSOLE_OT_insert", console_drop_path_poll, console_drop_path_copy, nullptr, nullptr);
203 "CONSOLE_OT_insert",
206 nullptr,
207 nullptr);
208}
209
210/* ************* end drop *********** */
211
212static void console_main_region_draw(const bContext *C, ARegion *region)
213{
214 /* draw entirely, view changes should be handled here */
216 View2D *v2d = &region->v2d;
217
220 (bContext *)C, "CONSOLE_OT_banner", WM_OP_EXEC_DEFAULT, nullptr, nullptr);
221 }
222
223 /* clear and setup matrix */
225
226 /* Works best with no view2d matrix set. */
228
229 /* data... */
230
231 console_history_verify(C); /* make sure we have some command line */
232 console_textview_main(sc, region);
233
234 /* reset view matrix */
236
237 /* scrollers */
238 UI_view2d_scrollers_draw(v2d, nullptr);
239}
240
265
266static void console_keymap(wmKeyConfig *keyconf)
267{
268 WM_keymap_ensure(keyconf, "Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
269}
270
271/****************** header region ******************/
272
273/* add handlers, stuff you only do once or on area/region changes */
275{
276 ED_region_header_init(region);
277}
278
279static void console_header_region_draw(const bContext *C, ARegion *region)
280{
281 ED_region_header(C, region);
282}
283
285{
286 ScrArea *area = params->area;
287 ARegion *region = params->region;
288 const wmNotifier *wmn = params->notifier;
289
290 /* context changes */
291 switch (wmn->category) {
292 case NC_SPACE: {
293 if (wmn->data == ND_SPACE_CONSOLE) {
294 if (wmn->action == NA_EDITED) {
295 if ((wmn->reference && area) && (wmn->reference == area->spacedata.first)) {
296 /* we've modified the geometry (font size), re-calculate rect */
297 console_textview_update_rect(static_cast<SpaceConsole *>(wmn->reference), region);
298 ED_region_tag_redraw(region);
299 }
300 }
301 else {
302 /* generic redraw request */
303 ED_region_tag_redraw(region);
304 }
305 }
306 break;
307 }
308 }
309}
310
312{
313 SpaceConsole *sconsole = (SpaceConsole *)sl;
314
315 BLO_read_struct_list(reader, ConsoleLine, &sconsole->scrollback);
316 BLO_read_struct_list(reader, ConsoleLine, &sconsole->history);
317
318 /* Comma expressions, (e.g. expr1, expr2, expr3) evaluate each expression,
319 * from left to right. the right-most expression sets the result of the comma
320 * expression as a whole. */
321 LISTBASE_FOREACH_MUTABLE (ConsoleLine *, cl, &sconsole->history) {
322 BLO_read_char_array(reader, size_t(cl->len) + 1, &cl->line);
323 if (cl->line) {
324 /* The allocated length is not written, so reset here. */
325 cl->len_alloc = cl->len + 1;
326 }
327 else {
328 BLI_remlink(&sconsole->history, cl);
329 MEM_freeN(cl);
330 }
331 }
332}
333
335{
336 SpaceConsole *con = (SpaceConsole *)sl;
337
338 LISTBASE_FOREACH (ConsoleLine *, cl, &con->history) {
339 /* 'len_alloc' is invalid on write, set from 'len' on read */
340 BLO_write_struct(writer, ConsoleLine, cl);
341 BLO_write_char_array(writer, size_t(cl->len) + 1, cl->line);
342 }
343 BLO_write_struct(writer, SpaceConsole, sl);
344}
345
347{
348 std::unique_ptr<SpaceType> st = std::make_unique<SpaceType>();
349 ARegionType *art;
350
351 st->spaceid = SPACE_CONSOLE;
352 STRNCPY(st->name, "Console");
353
354 st->create = console_create;
355 st->free = console_free;
356 st->init = console_init;
357 st->duplicate = console_duplicate;
358 st->operatortypes = console_operatortypes;
359 st->keymap = console_keymap;
360 st->dropboxes = console_dropboxes;
361 st->blend_read_data = console_blend_read_data;
362 st->blend_write = console_space_blend_write;
363
364 /* regions: main window */
365 art = static_cast<ARegionType *>(MEM_callocN(sizeof(ARegionType), "spacetype console region"));
368
371 art->cursor = console_cursor;
372 art->event_cursor = true;
374
375 BLI_addhead(&st->regiontypes, art);
376
377 /* regions: header */
378 art = static_cast<ARegionType *>(MEM_callocN(sizeof(ARegionType), "spacetype console region"));
380 art->prefsizey = HEADERY;
382
385
386 BLI_addhead(&st->regiontypes, art);
387
388 BKE_spacetype_register(std::move(st));
389}
SpaceConsole * CTX_wm_space_console(const bContext *C)
void BKE_spacetype_register(std::unique_ptr< SpaceType > st)
Definition screen.cc:268
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
#define FILE_MAX
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define BLO_write_struct(writer, struct_name, data_ptr)
void BLO_read_char_array(BlendDataReader *reader, int array_size, char **ptr_p)
Definition readfile.cc:4929
void BLO_write_char_array(BlendWriter *writer, uint num, const char *data_ptr)
#define BLO_read_struct_list(reader, struct_name, list)
#define HEADERY
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_TOP
@ RGN_TYPE_WINDOW
@ RGN_TYPE_HEADER
@ SPACE_CONSOLE
@ SPACE_EMPTY
@ USER_HEADER_BOTTOM
@ V2D_ALIGN_NO_NEG_X
@ V2D_ALIGN_NO_NEG_Y
@ V2D_LOCKOFS_X
@ V2D_SCROLL_RIGHT
@ V2D_KEEPTOT_BOUNDS
@ V2D_LIMITZOOM
@ V2D_LOCKZOOM_X
@ V2D_KEEPASPECT
@ V2D_LOCKZOOM_Y
void ED_region_header(const bContext *C, ARegion *region)
Definition area.cc:3646
void ED_region_header_init(ARegion *region)
Definition area.cc:3661
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
@ ED_KEYMAP_UI
Definition ED_screen.hh:725
@ ED_KEYMAP_HEADER
Definition ED_screen.hh:731
@ ED_KEYMAP_VIEW2D
Definition ED_screen.hh:728
Read Guarded memory(de)allocation.
@ 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:1605
void UI_view2d_view_restore(const bContext *C)
Definition view2d.cc:1158
void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
Definition view2d.cc:212
void UI_view2d_view_ortho(const View2D *v2d)
Definition view2d.cc:1091
@ V2D_COMMONVIEW_CUSTOM
Definition UI_view2d.hh:31
#define NA_EDITED
Definition WM_types.hh:550
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
@ WM_DRAG_PATH
Definition WM_types.hh:1160
@ WM_DRAG_STRING
Definition WM_types.hh:1169
#define ND_SPACE_CONSOLE
Definition WM_types.hh:485
#define NC_SPACE
Definition WM_types.hh:359
unsigned int U
Definition btGjkEpa3.h:78
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_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
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:1225
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)
Definition DNA_ID.h:413
void * first
eWM_DragDataType type
Definition WM_types.hh:1282
PointerRNA * ptr
Definition WM_types.hh:1368
unsigned int data
Definition WM_types.hh:325
unsigned int action
Definition WM_types.hh:325
unsigned int category
Definition WM_types.hh:325
void * reference
Definition WM_types.hh:327
struct wmKeyConfig * defaultconf
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)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:897
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))