1 Visual Class Library is responsible for the widgets (windowing, buttons, controls, file-pickers etc.), operating system abstraction, including basic rendering (e.g. the output device).
3 It should not be confused with Borland's Visual Component Library, which is entirely unrelated.
5 VCL provides a graphical toolkit similar to gtk+, Qt, SWING etc.
8 + the main cross-platform chunk of source
11 + cross-platform abstraction headers
14 + a backend renderer that draws to bitmaps
26 + code common to macOS and iOS
32 + Qt5 (under construction)
35 + X11 backend and its sub-platforms
39 + KF5 support (based on qt5 VCL plugin mentioned above)
41 + GTK3 support with KDE5 file pickers (alternative to native kf5 one)
46 + pluggable framework to select correct unx backend
49 + "data transfer" - clipboard handling
50 + http://stackoverflow.com/questions/3261379/getting-html-source-or-rich-text-from-the-x-clipboard
51 for tips how to show the current content of the
55 How the platform abstraction works
57 + InitVCL calls 'CreateSalInstance'
58 + this is implemented by the compiled-in platform backend
59 + it stores various bits of global state in the
60 'SalData' (inc/saldatabasic.hxx) structure but:
61 + the SalInstance vtable is the primary outward facing gateway
62 API for platform backends
63 + It is a factory for:
64 SalFrames, SalVirtualDevices, SalPrinters,
65 Timers, the SolarMutex, Drag&Drop and other
66 objects, as well as the primary event loop wrapper.
68 Note: references to "SV" in the code mean StarView, which was a
69 portable C++ class library for GUIs, with very old roots, that was
70 developed by StarDivision. Nowadays it is not used by anything except
71 LibreOffice (and OpenOffice).
73 "svp" stands for "StarView Plugin".
77 The way COM is used in LO generally:
78 - vcl puts main thread into Single-threaded Apartment (STA)
79 - oslWorkerWrapperFunction() puts every thread spawned via oslCreateThread()
80 into MTA (free-threaded)
84 GDIMetafile is a vector drawing representation that corresponds directly
85 to the SVM (StarView Metafile) format; it is extremely important as
86 an intermediate format in all sorts of drawing and printing operations.
88 There is a class MetafileXmlDump in include/vcl/mtfxmldump.hxx that
89 can store a GDIMetafile as XML, which makes debugging much easier
90 since you can just use "diff" to see changes.
94 emf+ is vector file format used by MSO and is successor of wmf and
96 http://msdn.microsoft.com/en-us/library/cc230724.aspx for
97 documentation. note that we didn't have this documentation from
98 start, so part of the code predates to the time when we had guessed
99 some parts and can be enhanced today. there also still many thing not
102 emf+ is handled a bit differently compared to original emf/wmf files,
103 because GDIMetafile is missing features we need (mostly related to
104 transparency, argb colors, etc.)
106 emf/wmf is translated to GDIMetafile in import filter
107 vcl/source/filter/wmf and so special handling ends here
109 emf+ is encapsulated into GDIMetafile inside comment records and
110 parsed/rendered later, when it reaches cppcanvas. It is parsed and
111 rendered in cppcanvas/source/mtfrenderer. also note that there are
112 emf+-only and emf+-dual files. dual files contains both types of
113 records (emf and emf+) for rendering the images. these can used also
114 in applications which don't know emf+. in that case we must ignore
115 emf records and use emf+ for rendering. for more details see
120 wmf/emf filter --> GDI metafile with emf+ in comments --> cppcanvas metafile renderer
122 lately the GDIMetafile rendering path changed which also influenced
123 emf+ rendering. now many things happen in drawing layer, where
124 GDIMetafile is translated into drawing layer primitives. for
125 metafiles with emf+ we let the mtfrenderer render them into bitmap
126 (with transparency) and use this bitmap in drawinlayer. cleaner
127 solution for current state would be to extend the drawing layer for
128 missing features and move parsing into drawing layer (might be quite
129 a lot of work). intermediary enhancement would be to know better the
130 needed size/resolution of the bitmap, before we render emf+ into
131 bitmap in drawing layer. Thorsten is working on the same problem with
132 svg rendering, so hopefully his approach could be extended for emf+ as
133 well. the places in drawing layer where we use canvas mtfrenderer to
134 render into bitmaps can be found when you grep for GetUseCanvas. also
135 look at vcl/source/gdi/gdimetafile.cxx where you can look for
136 UseCanvas again. moving the parsing into drawinglayer might also have
137 nice side effect for emf+-dual metafiles. in case the emf+ records
138 are broken, it would be easier to use the duplicit emf
139 rendering. fortunately we didn't run into such a broken emf+ file
140 yet. but there were already few cases where we first though that the
141 problem might be because of broken emf+ part. so far it always turned
142 out to be another problem.
148 vcl --> cppcanvas metafile renderer --> vcl
152 drawing layer --> vcl --> cppcanvas metafile renderer --> vcl --> drawing layer
154 another interesting part is actual rendering into canvas bitmap and
155 using that bitmap later in code using vcl API.
157 EMF+ implementation has some extensive logging, best if you do a dbgutil
160 export SAL_LOG=+INFO.cppcanvas.emf+INFO.vcl.emf
162 before running LibreOffice; it will give you lots of useful hints.
164 You can also fallback to EMF (from EMF+) rendering via
166 export EMF_PLUS_DISABLE=1
169 == Printing/PDF export ==
171 Printing from Writer works like this:
173 1) individual pages print by passing an appropriate OutputDevice to XRenderable
174 2) in drawinglayer, a VclMetafileProcessor2D is used to record everything on
175 the page (because the OutputDevice has been set up to record a GDIMetaFile)
176 3) the pages' GDIMetaFiles are converted to PDF by the vcl::PDFWriter
177 in vcl/source/gdi/pdfwriter*
179 Creating the ODF thumbnail for the first page works as above except step 3 is:
181 3) the GDIMetaFile is replayed to create the thumbnail
183 On-screen display differs in step 1 and 2:
185 1) the VCL Window gets invalidated somehow and paints itself
186 2) in drawinglayer, a VclPixelProcessor2D is used to display the content
189 === Debugging PDF export ===
191 Debugging the PDF export becomes much easier when
192 compression is disabled (so the PDF file is directly readable) and
193 the MARK function puts comments into the PDF file about which method
194 generated the following PDF content.
196 The compression can be disabled even using an env. var:
198 export VCL_DEBUG_DISABLE_PDFCOMPRESSION=1
200 To de-compress the contents of a PDF file written by a release build or
201 other programs, use the "pdfunzip" tool:
203 bin/run pdfunzip input.pdf output.pdf
205 === SolarMutexGuard ===
207 The solar mutex is the "big kernel lock" of LibreOffice, a global one. It's a
208 recursive mutex, so it's allowed to take the lock on the same thread multiple
209 times, and only the last unlock will actually release the mutex.
211 UNO methods on components can be called from multiple threads, while the
212 majority of the codebase is not prepared for multi-threading. One way to get
213 around this mismatch is to create a SolarMutexGuard instance at the start of
214 each & every UNO method implementation, but only when it is necessary:
216 - Only acquire the SolarMutex if you actually need it (e.g., not in functions
217 that return static information).
219 - Only around the code that actually needs it (i.e., never call out with it
222 This way you ensure that code (not prepared for multithreading) is still
223 executed only on a single thread.
225 In case you expect that your caller takes the solar mutex, then you can use
226 the DBG_TESTSOLARMUTEX() macro to assert that in dbgutil builds.
228 Event listeners are a special (but frequent) case of the "never call out with
229 a mutex (SolarMutex or other) locked" fundamental rule:
231 - UNO methods can be called from multiple threads, so most implementations
232 take the solar mutex as their first action when necessary.
234 - This can be problematic if later calling out (an event handler is called),
235 where the called function may be an UNO method implementation as well and
236 may be invoked on a different thread.
238 - So we try to not own the solar mutex, whenever we call out (invoke event
241 In short, never hold any mutex unless necessary, especially not when calling