editor_viewport.vala 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. namespace Crown
  6. {
  7. public enum ViewportRenderMode
  8. {
  9. CONTINUOUS,
  10. PUMPED,
  11. COUNT
  12. }
  13. public class EditorViewport : Gtk.Bin
  14. {
  15. public const string EDITOR_DISCONNECTED = "editor-disconnected";
  16. public const string EDITOR_OOPS = "editor-oops";
  17. public const GLib.ActionEntry[] actions =
  18. {
  19. { "camera-view", on_camera_view, "i", "0" }, // See: Crown.CameraViewType
  20. { "camera-frame-selected", on_camera_frame_selected, null, null },
  21. };
  22. public DatabaseEditor _database_editor;
  23. public Project _project;
  24. public string _boot_dir;
  25. public string _console_address;
  26. public uint16 _console_port;
  27. public ViewportRenderMode _render_mode;
  28. public bool _input_enabled;
  29. public RuntimeInstance _runtime;
  30. public EditorView _editor_view;
  31. public Gtk.Overlay _overlay;
  32. public Gtk.Stack _stack;
  33. public GLib.SimpleActionGroup _action_group;
  34. public EditorViewport(string name
  35. , DatabaseEditor database_editor
  36. , Project project
  37. , string boot_dir
  38. , string console_addr
  39. , uint16 console_port
  40. , ViewportRenderMode render_mode = ViewportRenderMode.PUMPED
  41. , bool input_enabled = true
  42. )
  43. {
  44. _database_editor = database_editor;
  45. _project = project;
  46. _boot_dir = boot_dir;
  47. _console_address = console_addr;
  48. _console_port = console_port;
  49. _render_mode = render_mode;
  50. _input_enabled = input_enabled;
  51. _runtime = new RuntimeInstance(name);
  52. _runtime.disconnected_unexpected.connect(on_editor_disconnected_unexpected);
  53. _overlay = new Gtk.Overlay();
  54. _stack = new Gtk.Stack();
  55. _stack.add_named(editor_disconnected(), EDITOR_DISCONNECTED);
  56. _stack.add_named(editor_oops(() => { restart_runtime.begin(); }), EDITOR_OOPS);
  57. _stack.set_visible_child_name(EDITOR_DISCONNECTED);
  58. _action_group = new GLib.SimpleActionGroup();
  59. _action_group.add_action_entries(actions, this);
  60. this.insert_action_group("viewport", _action_group);
  61. this.can_focus = true;
  62. this.add(_stack);
  63. }
  64. public void on_editor_disconnected_unexpected(RuntimeInstance ri)
  65. {
  66. _stack.set_visible_child_name(EDITOR_OOPS);
  67. }
  68. public async void start_runtime(uint window_xid, int width, int height)
  69. {
  70. if (window_xid == 0)
  71. return;
  72. // Spawn the level editor.
  73. string args[] =
  74. {
  75. ENGINE_EXE,
  76. "--data-dir",
  77. _project.data_dir(),
  78. "--boot-dir",
  79. _boot_dir,
  80. "--parent-window",
  81. window_xid.to_string(),
  82. "--console-port",
  83. _console_port.to_string(),
  84. "--wait-console",
  85. _render_mode == ViewportRenderMode.PUMPED ? "--pumped" : "",
  86. "--window-rect", "0", "0", width.to_string(), height.to_string(),
  87. };
  88. try {
  89. _runtime._process_id = _subprocess_launcher.spawnv_async(subprocess_flags(), args, ENGINE_DIR);
  90. } catch (Error e) {
  91. loge(e.message);
  92. }
  93. // Try to connect to the level editor.
  94. int tries = yield _runtime.connect_async(_console_address
  95. , _console_port
  96. , EDITOR_CONNECTION_TRIES
  97. , EDITOR_CONNECTION_INTERVAL
  98. );
  99. if (tries == EDITOR_CONNECTION_TRIES) {
  100. loge("Cannot connect to %s".printf(_runtime._name));
  101. return;
  102. }
  103. }
  104. public async void stop_runtime()
  105. {
  106. yield _runtime.stop();
  107. _stack.set_visible_child_name(EDITOR_DISCONNECTED);
  108. }
  109. public async void restart_runtime()
  110. {
  111. yield stop_runtime();
  112. if (_editor_view != null) {
  113. _overlay.remove(_editor_view);
  114. _stack.remove(_overlay);
  115. _editor_view = null;
  116. }
  117. _editor_view = new EditorView(_runtime, _input_enabled);
  118. _editor_view.native_window_ready.connect(on_editor_view_realized);
  119. _overlay.add(_editor_view);
  120. _overlay.show_all();
  121. _stack.add(_overlay);
  122. _stack.set_visible_child(_overlay);
  123. }
  124. public async void on_editor_view_realized(uint window_id, int width, int height)
  125. {
  126. start_runtime.begin(window_id, width, height);
  127. }
  128. public void frame()
  129. {
  130. if (_render_mode != ViewportRenderMode.PUMPED)
  131. return;
  132. _runtime.send(DeviceApi.frame());
  133. }
  134. public void on_camera_view(GLib.SimpleAction action, GLib.Variant? param)
  135. {
  136. CameraViewType view_type = (CameraViewType)param.get_int32();
  137. _runtime.send_script(LevelEditorApi.set_camera_view_type(view_type));
  138. frame();
  139. action.set_state(param);
  140. }
  141. public void on_camera_frame_selected(GLib.SimpleAction action, GLib.Variant? param)
  142. {
  143. Guid?[] selected_objects = _database_editor._selection.to_array();
  144. _runtime.send_script(LevelEditorApi.frame_objects(selected_objects));
  145. frame();
  146. }
  147. }
  148. } /* namespace Crown */